1 |
|
2 |
|
3 | #include <stdarg.h>
|
4 | #include <cstdlib>
|
5 | #include <cstring>
|
6 | #include <string.h>
|
7 | #include <stdlib.h>
|
8 |
|
9 | #ifdef __clang__
|
10 | #pragma clang diagnostic push
|
11 | #pragma clang diagnostic ignored "-Wunused-parameter"
|
12 | #endif
|
13 |
|
14 | #include <v8.h>
|
15 |
|
16 |
|
17 |
|
18 | #ifdef __clang__
|
19 | #pragma clang diagnostic pop
|
20 | #endif
|
21 |
|
22 | #include <node.h>
|
23 | #include <node_version.h>
|
24 | #include <node_buffer.h>
|
25 |
|
26 | #include <cmath>
|
27 | #include <iostream>
|
28 | #include <limits>
|
29 | #include <vector>
|
30 |
|
31 | #if defined(_WIN32) || defined(_WIN64)
|
32 |
|
33 | #include <malloc.h>
|
34 | #endif
|
35 |
|
36 | #if defined(__sun) || defined(_AIX)
|
37 | #include <alloca.h>
|
38 | #endif
|
39 |
|
40 | #include "nan.h"
|
41 | #include "bson.h"
|
42 |
|
43 | using namespace v8;
|
44 | using namespace node;
|
45 |
|
46 |
|
47 |
|
48 | void DataStream::WriteObjectId(const Local<Object>& object, const Local<String>& key)
|
49 | {
|
50 | uint16_t buffer[12];
|
51 | object->Get(key)->ToString()->Write(buffer, 0, 12);
|
52 | for(uint32_t i = 0; i < 12; ++i)
|
53 | {
|
54 | *p++ = (char) buffer[i];
|
55 | }
|
56 | }
|
57 |
|
58 | void ThrowAllocatedStringException(size_t allocationSize, const char* format, ...)
|
59 | {
|
60 | va_list info;
|
61 | va_start(info, format);
|
62 | char* string = (char*) malloc(allocationSize);
|
63 | vsprintf(string, format, info);
|
64 | va_end(info);
|
65 |
|
66 | throw string;
|
67 | }
|
68 |
|
69 | void DataStream::CheckKey(const Local<String>& keyName)
|
70 | {
|
71 | size_t keyLength = keyName->Utf8Length();
|
72 | if(keyLength == 0) return;
|
73 |
|
74 |
|
75 | char* keyStringBuffer = (char*) alloca(keyLength + 1);
|
76 |
|
77 | keyName->WriteUtf8(keyStringBuffer);
|
78 |
|
79 | char* terminator = strchr(keyStringBuffer, 0x00);
|
80 |
|
81 |
|
82 | if(terminator != &keyStringBuffer[keyLength]) {
|
83 | ThrowAllocatedStringException(64+keyLength, "key %s must not contain null bytes", keyStringBuffer);
|
84 | }
|
85 |
|
86 | if(keyStringBuffer[0] == '$')
|
87 | {
|
88 | ThrowAllocatedStringException(64+keyLength, "key %s must not start with '$'", keyStringBuffer);
|
89 | }
|
90 |
|
91 | if(strchr(keyStringBuffer, '.') != NULL)
|
92 | {
|
93 | ThrowAllocatedStringException(64+keyLength, "key %s must not contain '.'", keyStringBuffer);
|
94 | }
|
95 | }
|
96 |
|
97 | template<typename T> void BSONSerializer<T>::SerializeDocument(const Local<Value>& value)
|
98 | {
|
99 | void* documentSize = this->BeginWriteSize();
|
100 | Local<Object> object = bson->GetSerializeObject(value);
|
101 |
|
102 |
|
103 | Local<Array> propertyNames = object->GetPropertyNames();
|
104 |
|
105 |
|
106 | int propertyLength = propertyNames->Length();
|
107 | for(int i = 0; i < propertyLength; ++i)
|
108 | {
|
109 | const Local<String>& propertyName = propertyNames->Get(i)->ToString();
|
110 | if(checkKeys) this->CheckKey(propertyName);
|
111 |
|
112 | const Local<Value>& propertyValue = object->Get(propertyName);
|
113 |
|
114 | if(serializeFunctions || !propertyValue->IsFunction())
|
115 | {
|
116 | void* typeLocation = this->BeginWriteType();
|
117 | this->WriteString(propertyName);
|
118 | SerializeValue(typeLocation, propertyValue);
|
119 | }
|
120 | }
|
121 |
|
122 | this->WriteByte(0);
|
123 | this->CommitSize(documentSize);
|
124 | }
|
125 |
|
126 | template<typename T> void BSONSerializer<T>::SerializeArray(const Local<Value>& value)
|
127 | {
|
128 | void* documentSize = this->BeginWriteSize();
|
129 |
|
130 | Local<Array> array = Local<Array>::Cast(value->ToObject());
|
131 | uint32_t arrayLength = array->Length();
|
132 |
|
133 | for(uint32_t i = 0; i < arrayLength; ++i)
|
134 | {
|
135 | void* typeLocation = this->BeginWriteType();
|
136 | this->WriteUInt32String(i);
|
137 | SerializeValue(typeLocation, array->Get(i));
|
138 | }
|
139 |
|
140 | this->WriteByte(0);
|
141 | this->CommitSize(documentSize);
|
142 | }
|
143 |
|
144 |
|
145 |
|
146 |
|
147 | template<typename T> void BSONSerializer<T>::SerializeValue(void* typeLocation, const Local<Value>& value)
|
148 | {
|
149 | if(value->IsNumber())
|
150 | {
|
151 | double doubleValue = value->NumberValue();
|
152 | int intValue = (int) doubleValue;
|
153 | if(intValue == doubleValue)
|
154 | {
|
155 | this->CommitType(typeLocation, BSON_TYPE_INT);
|
156 | this->WriteInt32(intValue);
|
157 | }
|
158 | else
|
159 | {
|
160 | this->CommitType(typeLocation, BSON_TYPE_NUMBER);
|
161 | this->WriteDouble(doubleValue);
|
162 | }
|
163 | }
|
164 | else if(value->IsString())
|
165 | {
|
166 | this->CommitType(typeLocation, BSON_TYPE_STRING);
|
167 | this->WriteLengthPrefixedString(value->ToString());
|
168 | }
|
169 | else if(value->IsBoolean())
|
170 | {
|
171 | this->CommitType(typeLocation, BSON_TYPE_BOOLEAN);
|
172 | this->WriteBool(value);
|
173 | }
|
174 | else if(value->IsArray())
|
175 | {
|
176 | this->CommitType(typeLocation, BSON_TYPE_ARRAY);
|
177 | SerializeArray(value);
|
178 | }
|
179 | else if(value->IsDate())
|
180 | {
|
181 | this->CommitType(typeLocation, BSON_TYPE_DATE);
|
182 | this->WriteInt64(value);
|
183 | }
|
184 | else if(value->IsRegExp())
|
185 | {
|
186 | this->CommitType(typeLocation, BSON_TYPE_REGEXP);
|
187 | const Local<RegExp>& regExp = Local<RegExp>::Cast(value);
|
188 |
|
189 | this->WriteString(regExp->GetSource());
|
190 |
|
191 | int flags = regExp->GetFlags();
|
192 | if(flags & RegExp::kGlobal) this->WriteByte('s');
|
193 | if(flags & RegExp::kIgnoreCase) this->WriteByte('i');
|
194 | if(flags & RegExp::kMultiline) this->WriteByte('m');
|
195 | this->WriteByte(0);
|
196 | }
|
197 | else if(value->IsFunction())
|
198 | {
|
199 | this->CommitType(typeLocation, BSON_TYPE_CODE);
|
200 | this->WriteLengthPrefixedString(value->ToString());
|
201 | }
|
202 | else if(value->IsObject())
|
203 | {
|
204 | const Local<Object>& object = value->ToObject();
|
205 | if(object->Has(Nan::New(bson->_bsontypeString)))
|
206 | {
|
207 | const Local<String>& constructorString = object->GetConstructorName();
|
208 | if(Nan::New(bson->longString)->StrictEquals(constructorString))
|
209 | {
|
210 | this->CommitType(typeLocation, BSON_TYPE_LONG);
|
211 | this->WriteInt32(object, Nan::New(bson->_longLowString));
|
212 | this->WriteInt32(object, Nan::New(bson->_longHighString));
|
213 | }
|
214 | else if(Nan::New(bson->timestampString)->StrictEquals(constructorString))
|
215 | {
|
216 | this->CommitType(typeLocation, BSON_TYPE_TIMESTAMP);
|
217 | this->WriteInt32(object, Nan::New(bson->_longLowString));
|
218 | this->WriteInt32(object, Nan::New(bson->_longHighString));
|
219 | }
|
220 | else if(Nan::New(bson->objectIDString)->StrictEquals(constructorString))
|
221 | {
|
222 | this->CommitType(typeLocation, BSON_TYPE_OID);
|
223 | this->WriteObjectId(object, Nan::New(bson->_objectIDidString));
|
224 | }
|
225 | else if(Nan::New(bson->binaryString)->StrictEquals(constructorString))
|
226 | {
|
227 | this->CommitType(typeLocation, BSON_TYPE_BINARY);
|
228 |
|
229 | uint32_t length = object->Get(Nan::New(bson->_binaryPositionString))->Uint32Value();
|
230 | Local<Object> bufferObj = object->Get(Nan::New(bson->_binaryBufferString))->ToObject();
|
231 |
|
232 | this->WriteInt32(length);
|
233 | this->WriteByte(object, Nan::New(bson->_binarySubTypeString));
|
234 |
|
235 | if(object->Get(Nan::New(bson->_binarySubTypeString))->Int32Value() == 0x02) {
|
236 | this->WriteInt32(length);
|
237 | }
|
238 |
|
239 | this->WriteData(Buffer::Data(bufferObj), length);
|
240 | }
|
241 | else if(Nan::New(bson->doubleString)->StrictEquals(constructorString))
|
242 | {
|
243 | this->CommitType(typeLocation, BSON_TYPE_NUMBER);
|
244 | this->WriteDouble(object, Nan::New(bson->_doubleValueString));
|
245 | }
|
246 | else if(Nan::New(bson->symbolString)->StrictEquals(constructorString))
|
247 | {
|
248 | this->CommitType(typeLocation, BSON_TYPE_SYMBOL);
|
249 | this->WriteLengthPrefixedString(object->Get(Nan::New(bson->_symbolValueString))->ToString());
|
250 | }
|
251 | else if(Nan::New(bson->codeString)->StrictEquals(constructorString))
|
252 | {
|
253 | const Local<String>& function = object->Get(Nan::New(bson->_codeCodeString))->ToString();
|
254 | const Local<Object>& scope = object->Get(Nan::New(bson->_codeScopeString))->ToObject();
|
255 |
|
256 |
|
257 | #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 6
|
258 | uint32_t propertyNameLength = scope->GetPropertyNames()->Length();
|
259 | #else
|
260 | uint32_t propertyNameLength = scope->GetOwnPropertyNames()->Length();
|
261 | #endif
|
262 |
|
263 | if(propertyNameLength > 0)
|
264 | {
|
265 | this->CommitType(typeLocation, BSON_TYPE_CODE_W_SCOPE);
|
266 | void* codeWidthScopeSize = this->BeginWriteSize();
|
267 | this->WriteLengthPrefixedString(function->ToString());
|
268 | SerializeDocument(scope);
|
269 | this->CommitSize(codeWidthScopeSize);
|
270 | }
|
271 | else
|
272 | {
|
273 | this->CommitType(typeLocation, BSON_TYPE_CODE);
|
274 | this->WriteLengthPrefixedString(function->ToString());
|
275 | }
|
276 | }
|
277 | else if(Nan::New(bson->dbrefString)->StrictEquals(constructorString))
|
278 | {
|
279 | this->CommitType(typeLocation, BSON_TYPE_OBJECT);
|
280 |
|
281 | void* dbRefSize = this->BeginWriteSize();
|
282 |
|
283 | void* refType = this->BeginWriteType();
|
284 | this->WriteData("$ref", 5);
|
285 | SerializeValue(refType, object->Get(Nan::New(bson->_dbRefNamespaceString)));
|
286 |
|
287 | void* idType = this->BeginWriteType();
|
288 | this->WriteData("$id", 4);
|
289 | SerializeValue(idType, object->Get(Nan::New(bson->_dbRefOidString)));
|
290 |
|
291 | const Local<Value>& refDbValue = object->Get(Nan::New(bson->_dbRefDbString));
|
292 | if(!refDbValue->IsUndefined())
|
293 | {
|
294 | void* dbType = this->BeginWriteType();
|
295 | this->WriteData("$db", 4);
|
296 | SerializeValue(dbType, refDbValue);
|
297 | }
|
298 |
|
299 | this->WriteByte(0);
|
300 | this->CommitSize(dbRefSize);
|
301 | }
|
302 | else if(Nan::New(bson->minKeyString)->StrictEquals(constructorString))
|
303 | {
|
304 | this->CommitType(typeLocation, BSON_TYPE_MIN_KEY);
|
305 | }
|
306 | else if(Nan::New(bson->maxKeyString)->StrictEquals(constructorString))
|
307 | {
|
308 | this->CommitType(typeLocation, BSON_TYPE_MAX_KEY);
|
309 | }
|
310 | }
|
311 | else if(Buffer::HasInstance(value))
|
312 | {
|
313 | this->CommitType(typeLocation, BSON_TYPE_BINARY);
|
314 |
|
315 | #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 3
|
316 | Local<Object> buffer = ObjectWrap::Unwrap<Buffer>(value->ToObject());
|
317 | uint32_t length = object->length();
|
318 | #else
|
319 | uint32_t length = Buffer::Length(value->ToObject());
|
320 | #endif
|
321 |
|
322 | this->WriteInt32(length);
|
323 | this->WriteByte(0);
|
324 | this->WriteData(Buffer::Data(value->ToObject()), length);
|
325 | }
|
326 | else
|
327 | {
|
328 | this->CommitType(typeLocation, BSON_TYPE_OBJECT);
|
329 | SerializeDocument(value);
|
330 | }
|
331 | }
|
332 | else if(value->IsNull() || value->IsUndefined())
|
333 | {
|
334 | this->CommitType(typeLocation, BSON_TYPE_NULL);
|
335 | }
|
336 | }
|
337 |
|
338 |
|
339 | BSONDeserializer::BSONDeserializer(BSON* aBson, char* data, size_t length)
|
340 | : bson(aBson),
|
341 | pStart(data),
|
342 | p(data),
|
343 | pEnd(data + length - 1)
|
344 | {
|
345 | if(*pEnd != '\0') ThrowAllocatedStringException(64, "Missing end of document marker '\\0'");
|
346 | }
|
347 |
|
348 | BSONDeserializer::BSONDeserializer(BSONDeserializer& parentSerializer, size_t length)
|
349 | : bson(parentSerializer.bson),
|
350 | pStart(parentSerializer.p),
|
351 | p(parentSerializer.p),
|
352 | pEnd(parentSerializer.p + length - 1)
|
353 | {
|
354 | parentSerializer.p += length;
|
355 | if(pEnd > parentSerializer.pEnd) ThrowAllocatedStringException(64, "Child document exceeds parent's bounds");
|
356 | if(*pEnd != '\0') ThrowAllocatedStringException(64, "Missing end of document marker '\\0'");
|
357 | }
|
358 |
|
359 | Local<Value> BSONDeserializer::ReadCString()
|
360 | {
|
361 | char* start = p;
|
362 | while(*p++ && (p < pEnd)) { }
|
363 | if(p > pEnd) {
|
364 | return Nan::Null();
|
365 | }
|
366 | return Nan::New<String>(start, (int32_t) (p-start-1) ).ToLocalChecked();
|
367 | }
|
368 |
|
369 | int32_t BSONDeserializer::ReadRegexOptions()
|
370 | {
|
371 | int32_t options = 0;
|
372 | for(;;)
|
373 | {
|
374 | switch(*p++)
|
375 | {
|
376 | case '\0': return options;
|
377 | case 's': options |= RegExp::kGlobal; break;
|
378 | case 'i': options |= RegExp::kIgnoreCase; break;
|
379 | case 'm': options |= RegExp::kMultiline; break;
|
380 | }
|
381 | }
|
382 | }
|
383 |
|
384 | uint32_t BSONDeserializer::ReadIntegerString()
|
385 | {
|
386 | uint32_t value = 0;
|
387 | while(*p)
|
388 | {
|
389 | if(*p < '0' || *p > '9') ThrowAllocatedStringException(64, "Invalid key for array");
|
390 | value = value * 10 + *p++ - '0';
|
391 | }
|
392 | ++p;
|
393 | return value;
|
394 | }
|
395 |
|
396 | Local<String> BSONDeserializer::ReadString()
|
397 | {
|
398 | uint32_t length = ReadUInt32();
|
399 | char* start = p;
|
400 | p += length;
|
401 | return Nan::New<String>(start, length-1).ToLocalChecked();
|
402 | }
|
403 |
|
404 | Local<String> BSONDeserializer::ReadObjectId()
|
405 | {
|
406 | uint16_t objectId[12];
|
407 | for(size_t i = 0; i < 12; ++i)
|
408 | {
|
409 | objectId[i] = *reinterpret_cast<unsigned char*>(p++);
|
410 | }
|
411 | return Nan::New<String>(objectId, 12).ToLocalChecked();
|
412 | }
|
413 |
|
414 | Local<Value> BSONDeserializer::DeserializeDocument(bool promoteLongs)
|
415 | {
|
416 | uint32_t length = ReadUInt32();
|
417 | if(length < 5) ThrowAllocatedStringException(64, "Bad BSON: Document is less than 5 bytes");
|
418 |
|
419 | BSONDeserializer documentDeserializer(*this, length-4);
|
420 | return documentDeserializer.DeserializeDocumentInternal(promoteLongs);
|
421 | }
|
422 |
|
423 | Local<Value> BSONDeserializer::DeserializeDocumentInternal(bool promoteLongs)
|
424 | {
|
425 | Local<Object> returnObject = Nan::New<Object>();
|
426 |
|
427 | while(HasMoreData())
|
428 | {
|
429 | BsonType type = (BsonType) ReadByte();
|
430 | const Local<Value>& name = ReadCString();
|
431 | if(name->IsNull()) ThrowAllocatedStringException(64, "Bad BSON Document: illegal CString");
|
432 |
|
433 | const Local<Value>& value = DeserializeValue(type, promoteLongs);
|
434 | Nan::DefineOwnProperty(returnObject, ((Local<Value>&)name).As<v8::String>(), value, v8::None);
|
435 | }
|
436 | if(p != pEnd) ThrowAllocatedStringException(64, "Bad BSON Document: Serialize consumed unexpected number of bytes");
|
437 |
|
438 |
|
439 |
|
440 | if(returnObject->Has(Nan::New(bson->_dbRefIdRefString)))
|
441 | {
|
442 | Local<Value> argv[] = { returnObject->Get(Nan::New(bson->_dbRefRefString)), returnObject->Get(Nan::New(bson->_dbRefIdRefString)), returnObject->Get(Nan::New(bson->_dbRefDbRefString)) };
|
443 | return Nan::NewInstance(Nan::New(bson->dbrefConstructor), 3, argv).ToLocalChecked();
|
444 | }
|
445 | else
|
446 | {
|
447 | return returnObject;
|
448 | }
|
449 | }
|
450 |
|
451 | Local<Value> BSONDeserializer::DeserializeArray(bool promoteLongs)
|
452 | {
|
453 | uint32_t length = ReadUInt32();
|
454 | if(length < 5) ThrowAllocatedStringException(64, "Bad BSON: Array Document is less than 5 bytes");
|
455 |
|
456 | BSONDeserializer documentDeserializer(*this, length-4);
|
457 | return documentDeserializer.DeserializeArrayInternal(promoteLongs);
|
458 | }
|
459 |
|
460 | Local<Value> BSONDeserializer::DeserializeArrayInternal(bool promoteLongs)
|
461 | {
|
462 | Local<Array> returnArray = Nan::New<Array>();
|
463 |
|
464 | while(HasMoreData())
|
465 | {
|
466 | BsonType type = (BsonType) ReadByte();
|
467 | uint32_t index = ReadIntegerString();
|
468 | const Local<Value>& value = DeserializeValue(type, promoteLongs);
|
469 | returnArray->Set(index, value);
|
470 | }
|
471 | if(p != pEnd) ThrowAllocatedStringException(64, "Bad BSON Array: Serialize consumed unexpected number of bytes");
|
472 |
|
473 | return returnArray;
|
474 | }
|
475 |
|
476 | Local<Value> BSONDeserializer::DeserializeValue(BsonType type, bool promoteLongs)
|
477 | {
|
478 | switch(type)
|
479 | {
|
480 | case BSON_TYPE_STRING:
|
481 | return ReadString();
|
482 |
|
483 | case BSON_TYPE_INT:
|
484 | return Nan::New<Integer>(ReadInt32());
|
485 |
|
486 | case BSON_TYPE_NUMBER:
|
487 | return Nan::New<Number>(ReadDouble());
|
488 |
|
489 | case BSON_TYPE_NULL:
|
490 | return Nan::Null();
|
491 |
|
492 | case BSON_TYPE_UNDEFINED:
|
493 | return Nan::Undefined();
|
494 |
|
495 | case BSON_TYPE_TIMESTAMP:
|
496 | {
|
497 | int32_t lowBits = ReadInt32();
|
498 | int32_t highBits = ReadInt32();
|
499 | Local<Value> argv[] = { Nan::New<Int32>(lowBits), Nan::New<Int32>(highBits) };
|
500 | return Nan::NewInstance(Nan::New(bson->timestampConstructor), 2, argv).ToLocalChecked();
|
501 | }
|
502 |
|
503 | case BSON_TYPE_BOOLEAN:
|
504 | return (ReadByte() != 0) ? Nan::True() : Nan::False();
|
505 |
|
506 | case BSON_TYPE_REGEXP:
|
507 | {
|
508 | const Local<Value>& regex = ReadCString();
|
509 | if(regex->IsNull()) ThrowAllocatedStringException(64, "Bad BSON Document: illegal CString");
|
510 | int32_t options = ReadRegexOptions();
|
511 | return RegExp::New(regex->ToString(), (RegExp::Flags) options);
|
512 | }
|
513 |
|
514 | case BSON_TYPE_CODE:
|
515 | {
|
516 | const Local<Value>& code = ReadString();
|
517 | const Local<Value>& scope = Nan::New<Object>();
|
518 | Local<Value> argv[] = { code, scope };
|
519 | return Nan::NewInstance(Nan::New(bson->codeConstructor), 2, argv).ToLocalChecked();
|
520 | }
|
521 |
|
522 | case BSON_TYPE_CODE_W_SCOPE:
|
523 | {
|
524 | ReadUInt32();
|
525 | const Local<Value>& code = ReadString();
|
526 | const Local<Value>& scope = DeserializeDocument(promoteLongs);
|
527 | Local<Value> argv[] = { code, scope->ToObject() };
|
528 | return Nan::NewInstance(Nan::New(bson->codeConstructor), 2, argv).ToLocalChecked();
|
529 | }
|
530 |
|
531 | case BSON_TYPE_OID:
|
532 | {
|
533 | Local<Value> argv[] = { ReadObjectId() };
|
534 | return Nan::NewInstance(Nan::New(bson->objectIDConstructor), 1, argv).ToLocalChecked();
|
535 | }
|
536 |
|
537 | case BSON_TYPE_BINARY:
|
538 | {
|
539 | uint32_t length = ReadUInt32();
|
540 | uint32_t subType = ReadByte();
|
541 | if(subType == 0x02) {
|
542 | length = ReadInt32();
|
543 | }
|
544 |
|
545 | Local<Object> buffer = Nan::NewBuffer(p, length).ToLocalChecked();
|
546 | p += length;
|
547 |
|
548 | Local<Value> argv[] = { buffer, Nan::New<Uint32>(subType) };
|
549 | return Nan::NewInstance(Nan::New(bson->binaryConstructor), 2, argv).ToLocalChecked();
|
550 | }
|
551 |
|
552 | case BSON_TYPE_LONG:
|
553 | {
|
554 |
|
555 | int32_t lowBits = (int32_t) ReadInt32();
|
556 | int32_t highBits = (int32_t) ReadInt32();
|
557 |
|
558 |
|
559 | if(promoteLongs) {
|
560 |
|
561 | if((highBits < 0x200000 || (highBits == 0x200000 && lowBits == 0)) && highBits >= -0x200000) {
|
562 |
|
563 | p -= 8;
|
564 |
|
565 | int64_t finalValue = (int64_t) ReadInt64();
|
566 | return Nan::New<Number>(finalValue);
|
567 | }
|
568 | }
|
569 |
|
570 |
|
571 | Local<Value> argv[] = { Nan::New<Int32>(lowBits), Nan::New<Int32>(highBits) };
|
572 | return Nan::NewInstance(Nan::New(bson->longConstructor), 2, argv).ToLocalChecked();
|
573 | }
|
574 |
|
575 | case BSON_TYPE_DATE:
|
576 | return Nan::New<Date>((double) ReadInt64()).ToLocalChecked();
|
577 |
|
578 | case BSON_TYPE_ARRAY:
|
579 | return DeserializeArray(promoteLongs);
|
580 |
|
581 | case BSON_TYPE_OBJECT:
|
582 | return DeserializeDocument(promoteLongs);
|
583 |
|
584 | case BSON_TYPE_SYMBOL:
|
585 | {
|
586 | const Local<String>& string = ReadString();
|
587 | Local<Value> argv[] = { string };
|
588 | return Nan::NewInstance(Nan::New(bson->symbolConstructor), 1, argv).ToLocalChecked();
|
589 | }
|
590 |
|
591 | case BSON_TYPE_MIN_KEY:
|
592 | return Nan::NewInstance(Nan::New(bson->minKeyConstructor)).ToLocalChecked();
|
593 |
|
594 | case BSON_TYPE_MAX_KEY:
|
595 | return Nan::NewInstance(Nan::New(bson->maxKeyConstructor)).ToLocalChecked();
|
596 |
|
597 | default:
|
598 | ThrowAllocatedStringException(64, "Unhandled BSON Type: %d", type);
|
599 | }
|
600 |
|
601 | return Nan::Null();
|
602 | }
|
603 |
|
604 | Nan::Persistent<FunctionTemplate> BSON::constructor_template;
|
605 |
|
606 | BSON::BSON() : ObjectWrap()
|
607 | {
|
608 |
|
609 | _bsontypeString.Reset(Nan::New<String>("_bsontype").ToLocalChecked());
|
610 | _longLowString.Reset(Nan::New<String>("low_").ToLocalChecked());
|
611 | _longHighString.Reset(Nan::New<String>("high_").ToLocalChecked());
|
612 | _objectIDidString.Reset(Nan::New<String>("id").ToLocalChecked());
|
613 | _binaryPositionString.Reset(Nan::New<String>("position").ToLocalChecked());
|
614 | _binarySubTypeString.Reset(Nan::New<String>("sub_type").ToLocalChecked());
|
615 | _binaryBufferString.Reset(Nan::New<String>("buffer").ToLocalChecked());
|
616 | _doubleValueString.Reset(Nan::New<String>("value").ToLocalChecked());
|
617 | _symbolValueString.Reset(Nan::New<String>("value").ToLocalChecked());
|
618 | _dbRefRefString.Reset(Nan::New<String>("$ref").ToLocalChecked());
|
619 | _dbRefIdRefString.Reset(Nan::New<String>("$id").ToLocalChecked());
|
620 | _dbRefDbRefString.Reset(Nan::New<String>("$db").ToLocalChecked());
|
621 | _dbRefNamespaceString.Reset(Nan::New<String>("namespace").ToLocalChecked());
|
622 | _dbRefDbString.Reset(Nan::New<String>("db").ToLocalChecked());
|
623 | _dbRefOidString.Reset(Nan::New<String>("oid").ToLocalChecked());
|
624 | _codeCodeString.Reset(Nan::New<String>("code").ToLocalChecked());
|
625 | _codeScopeString.Reset(Nan::New<String>("scope").ToLocalChecked());
|
626 | _toBSONString.Reset(Nan::New<String>("toBSON").ToLocalChecked());
|
627 |
|
628 | longString.Reset(Nan::New<String>("Long").ToLocalChecked());
|
629 | objectIDString.Reset(Nan::New<String>("ObjectID").ToLocalChecked());
|
630 | binaryString.Reset(Nan::New<String>("Binary").ToLocalChecked());
|
631 | codeString.Reset(Nan::New<String>("Code").ToLocalChecked());
|
632 | dbrefString.Reset(Nan::New<String>("DBRef").ToLocalChecked());
|
633 | symbolString.Reset(Nan::New<String>("Symbol").ToLocalChecked());
|
634 | doubleString.Reset(Nan::New<String>("Double").ToLocalChecked());
|
635 | timestampString.Reset(Nan::New<String>("Timestamp").ToLocalChecked());
|
636 | minKeyString.Reset(Nan::New<String>("MinKey").ToLocalChecked());
|
637 | maxKeyString.Reset(Nan::New<String>("MaxKey").ToLocalChecked());
|
638 | }
|
639 |
|
640 | BSON::~BSON() {
|
641 | _bsontypeString.Reset();
|
642 | _longLowString.Reset();
|
643 | _longHighString.Reset();
|
644 | _objectIDidString.Reset();
|
645 | _binaryPositionString.Reset();
|
646 | _binarySubTypeString.Reset();
|
647 | _binaryBufferString.Reset();
|
648 | _doubleValueString.Reset();
|
649 | _symbolValueString.Reset();
|
650 | _dbRefRefString.Reset();
|
651 | _dbRefIdRefString.Reset();
|
652 | _dbRefDbRefString.Reset();
|
653 | _dbRefNamespaceString.Reset();
|
654 | _dbRefDbString.Reset();
|
655 | _dbRefOidString.Reset();
|
656 | _codeCodeString.Reset();
|
657 | _codeScopeString.Reset();
|
658 | _toBSONString.Reset();
|
659 |
|
660 | longString.Reset();
|
661 | objectIDString.Reset();
|
662 | binaryString.Reset();
|
663 | codeString.Reset();
|
664 | dbrefString.Reset();
|
665 | symbolString.Reset();
|
666 | doubleString.Reset();
|
667 | timestampString.Reset();
|
668 | minKeyString.Reset();
|
669 | maxKeyString.Reset();
|
670 | }
|
671 |
|
672 | void BSON::Initialize(v8::Local<v8::Object> target)
|
673 | {
|
674 |
|
675 | Nan::HandleScope scope;
|
676 |
|
677 | Local<FunctionTemplate> t = Nan::New<FunctionTemplate>();
|
678 | t->InstanceTemplate()->SetInternalFieldCount(1);
|
679 | t->SetClassName(Nan::New<String>("BSON").ToLocalChecked());
|
680 |
|
681 |
|
682 | Nan::SetPrototypeMethod(t, "calculateObjectSize", CalculateObjectSize);
|
683 | Nan::SetPrototypeMethod(t, "serialize", BSONSerialize);
|
684 | Nan::SetPrototypeMethod(t, "serializeWithBufferAndIndex", SerializeWithBufferAndIndex);
|
685 | Nan::SetPrototypeMethod(t, "deserialize", BSONDeserialize);
|
686 | Nan::SetPrototypeMethod(t, "deserializeStream", BSONDeserializeStream);
|
687 |
|
688 | constructor_template.Reset(t);
|
689 |
|
690 | Nan::DefineOwnProperty(target, Nan::New<String>("BSON").ToLocalChecked(), t->GetFunction(), v8::None);
|
691 | }
|
692 |
|
693 |
|
694 | NAN_METHOD(BSON::New)
|
695 | {
|
696 | Nan::HandleScope scope;
|
697 |
|
698 |
|
699 | if(info.Length() == 1 && info[0]->IsArray())
|
700 | {
|
701 |
|
702 | Local<Array> array = Local<Array>::Cast(info[0]);
|
703 |
|
704 | if(array->Length() > 0)
|
705 | {
|
706 |
|
707 | BSON *bson = new BSON();
|
708 |
|
709 | uint32_t foundClassesMask = 0;
|
710 |
|
711 |
|
712 | for(uint32_t i = 0; i < array->Length(); i++) {
|
713 |
|
714 | Local<Function> func = Local<Function>::Cast(array->Get(i));
|
715 | Local<String> functionName = func->GetName()->ToString();
|
716 |
|
717 |
|
718 | if(functionName->StrictEquals(Nan::New(bson->longString))) {
|
719 | bson->longConstructor.Reset(func);
|
720 | foundClassesMask |= 1;
|
721 | } else if(functionName->StrictEquals(Nan::New(bson->objectIDString))) {
|
722 | bson->objectIDConstructor.Reset(func);
|
723 | foundClassesMask |= 2;
|
724 | } else if(functionName->StrictEquals(Nan::New(bson->binaryString))) {
|
725 | bson->binaryConstructor.Reset(func);
|
726 | foundClassesMask |= 4;
|
727 | } else if(functionName->StrictEquals(Nan::New(bson->codeString))) {
|
728 | bson->codeConstructor.Reset(func);
|
729 | foundClassesMask |= 8;
|
730 | } else if(functionName->StrictEquals(Nan::New(bson->dbrefString))) {
|
731 | bson->dbrefConstructor.Reset(func);
|
732 | foundClassesMask |= 0x10;
|
733 | } else if(functionName->StrictEquals(Nan::New(bson->symbolString))) {
|
734 | bson->symbolConstructor.Reset(func);
|
735 | foundClassesMask |= 0x20;
|
736 | } else if(functionName->StrictEquals(Nan::New(bson->doubleString))) {
|
737 | bson->doubleConstructor.Reset(func);
|
738 | foundClassesMask |= 0x40;
|
739 | } else if(functionName->StrictEquals(Nan::New(bson->timestampString))) {
|
740 | bson->timestampConstructor.Reset(func);
|
741 | foundClassesMask |= 0x80;
|
742 | } else if(functionName->StrictEquals(Nan::New(bson->minKeyString))) {
|
743 | bson->minKeyConstructor.Reset(func);
|
744 | foundClassesMask |= 0x100;
|
745 | } else if(functionName->StrictEquals(Nan::New(bson->maxKeyString))) {
|
746 | bson->maxKeyConstructor.Reset(func);
|
747 | foundClassesMask |= 0x200;
|
748 | }
|
749 | }
|
750 |
|
751 |
|
752 | if(foundClassesMask != 0x3ff) {
|
753 | delete bson;
|
754 | return Nan::ThrowError("Missing function constructor for either [Long/ObjectID/Binary/Code/DbRef/Symbol/Double/Timestamp/MinKey/MaxKey]");
|
755 | } else {
|
756 | bson->Wrap(info.This());
|
757 | info.GetReturnValue().Set(info.This());
|
758 | }
|
759 | }
|
760 | else
|
761 | {
|
762 | return Nan::ThrowError("No types passed in");
|
763 | }
|
764 | }
|
765 | else
|
766 | {
|
767 | return Nan::ThrowTypeError("Argument passed in must be an array of types");
|
768 | }
|
769 | }
|
770 |
|
771 |
|
772 |
|
773 |
|
774 |
|
775 |
|
776 | NAN_METHOD(BSON::BSONDeserialize)
|
777 | {
|
778 | Nan::HandleScope scope;
|
779 |
|
780 |
|
781 | if(info.Length() > 1 && !info[0]->IsString() && !Buffer::HasInstance(info[0]))
|
782 | return Nan::ThrowError("First Argument must be a Buffer or String.");
|
783 |
|
784 |
|
785 | bool promoteLongs = true;
|
786 |
|
787 |
|
788 | if(info.Length() == 2 && info[1]->IsObject()) {
|
789 | Local<Object> options = info[1]->ToObject();
|
790 |
|
791 | if(options->Has(Nan::New<String>("promoteLongs").ToLocalChecked())) {
|
792 | promoteLongs = options->Get(Nan::New<String>("promoteLongs").ToLocalChecked())->ToBoolean()->Value();
|
793 | }
|
794 | }
|
795 |
|
796 |
|
797 | Local<Object> obj = info[0]->ToObject();
|
798 |
|
799 |
|
800 | BSON *bson = ObjectWrap::Unwrap<BSON>(info.This());
|
801 |
|
802 |
|
803 | if(Buffer::HasInstance(obj))
|
804 | {
|
805 | #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 3
|
806 | Local<Object> buffer = ObjectWrap::Unwrap<Buffer>(obj);
|
807 | char* data = buffer->data();
|
808 | size_t length = buffer->length();
|
809 | #else
|
810 | char* data = Buffer::Data(obj);
|
811 | size_t length = Buffer::Length(obj);
|
812 | #endif
|
813 |
|
814 |
|
815 | if(length < 5) return Nan::ThrowError("corrupt bson message < 5 bytes long");
|
816 |
|
817 | try
|
818 | {
|
819 | BSONDeserializer deserializer(bson, data, length);
|
820 |
|
821 | info.GetReturnValue().Set(deserializer.DeserializeDocument(promoteLongs));
|
822 | }
|
823 | catch(char* exception)
|
824 | {
|
825 | Local<String> error = Nan::New<String>(exception).ToLocalChecked();
|
826 | free(exception);
|
827 | return Nan::ThrowError(error);
|
828 | }
|
829 |
|
830 | }
|
831 | else
|
832 | {
|
833 |
|
834 | intptr_t len = Nan::DecodeBytes(info[0], (Nan::Encoding)BINARY);
|
835 |
|
836 |
|
837 | if(len < 5) return Nan::ThrowError("corrupt bson message < 5 bytes long");
|
838 |
|
839 |
|
840 | char* data = (char *)malloc(len);
|
841 | Nan::DecodeWrite(data, len, info[0], (Nan::Encoding)BINARY);
|
842 |
|
843 | try
|
844 | {
|
845 | BSONDeserializer deserializer(bson, data, len);
|
846 |
|
847 | Local<Value> result = deserializer.DeserializeDocument(promoteLongs);
|
848 | free(data);
|
849 | info.GetReturnValue().Set(result);
|
850 |
|
851 | }
|
852 | catch(char* exception)
|
853 | {
|
854 | Local<String> error = Nan::New<String>(exception).ToLocalChecked();
|
855 | free(exception);
|
856 | free(data);
|
857 | return Nan::ThrowError(error);
|
858 | }
|
859 | }
|
860 | }
|
861 |
|
862 | Local<Object> BSON::GetSerializeObject(const Local<Value>& argValue)
|
863 | {
|
864 | Local<Object> object = argValue->ToObject();
|
865 | if(object->Has(Nan::New(_toBSONString)))
|
866 | {
|
867 | const Local<Value>& toBSON = object->Get(Nan::New(_toBSONString));
|
868 | if(!toBSON->IsFunction()) ThrowAllocatedStringException(64, "toBSON is not a function");
|
869 |
|
870 | Local<Value> result = Local<Function>::Cast(toBSON)->Call(object, 0, NULL);
|
871 | if(!result->IsObject()) ThrowAllocatedStringException(64, "toBSON function did not return an object");
|
872 | return result->ToObject();
|
873 | }
|
874 | else
|
875 | {
|
876 | return object;
|
877 | }
|
878 | }
|
879 |
|
880 | NAN_METHOD(BSON::BSONSerialize)
|
881 | {
|
882 | Nan::HandleScope scope;
|
883 |
|
884 | if(info.Length() == 1 && !info[0]->IsObject()) return Nan::ThrowError("One, two or tree arguments required - [object] or [object, boolean] or [object, boolean, boolean]");
|
885 | if(info.Length() == 2 && !info[0]->IsObject() && !info[1]->IsBoolean()) return Nan::ThrowError("One, two or tree arguments required - [object] or [object, boolean] or [object, boolean, boolean]");
|
886 | if(info.Length() == 3 && !info[0]->IsObject() && !info[1]->IsBoolean() && !info[2]->IsBoolean()) return Nan::ThrowError("One, two or tree arguments required - [object] or [object, boolean] or [object, boolean, boolean]");
|
887 | if(info.Length() == 4 && !info[0]->IsObject() && !info[1]->IsBoolean() && !info[2]->IsBoolean() && !info[3]->IsBoolean()) return Nan::ThrowError("One, two or tree arguments required - [object] or [object, boolean] or [object, boolean, boolean] or [object, boolean, boolean, boolean]");
|
888 | if(info.Length() > 4) return Nan::ThrowError("One, two, tree or four arguments required - [object] or [object, boolean] or [object, boolean, boolean] or [object, boolean, boolean, boolean]");
|
889 |
|
890 |
|
891 | if(info[0]->IsArray()) return Nan::ThrowError("Only javascript objects supported");
|
892 |
|
893 |
|
894 | BSON *bson = ObjectWrap::Unwrap<BSON>(info.This());
|
895 |
|
896 |
|
897 |
|
898 | bool serializeFunctions = (info.Length() >= 4) && info[3]->BooleanValue();
|
899 |
|
900 | char *serialized_object = NULL;
|
901 | size_t object_size;
|
902 | try
|
903 | {
|
904 | Local<Object> object = bson->GetSerializeObject(info[0]);
|
905 |
|
906 | BSONSerializer<CountStream> counter(bson, false, serializeFunctions);
|
907 | counter.SerializeDocument(object);
|
908 | object_size = counter.GetSerializeSize();
|
909 |
|
910 |
|
911 | serialized_object = (char *)malloc(object_size);
|
912 |
|
913 |
|
914 | bool checkKeys = info.Length() >= 3 && info[1]->IsBoolean() && info[1]->BooleanValue();
|
915 | BSONSerializer<DataStream> data(bson, checkKeys, serializeFunctions, serialized_object);
|
916 | data.SerializeDocument(object);
|
917 | }
|
918 | catch(char *err_msg)
|
919 | {
|
920 | free(serialized_object);
|
921 | Local<String> error = Nan::New<String>(err_msg).ToLocalChecked();
|
922 | free(err_msg);
|
923 | return Nan::ThrowError(error);
|
924 | }
|
925 |
|
926 |
|
927 | if(info.Length() == 3 || info.Length() == 4)
|
928 | {
|
929 | Local<Object> buffer = Nan::NewBuffer(serialized_object, object_size).ToLocalChecked();
|
930 | free(serialized_object);
|
931 | info.GetReturnValue().Set(buffer);
|
932 | }
|
933 | else
|
934 | {
|
935 | Local<Value> bin_value = Nan::Encode(serialized_object, object_size, (Nan::Encoding)BINARY)->ToString();
|
936 | free(serialized_object);
|
937 | info.GetReturnValue().Set(bin_value);
|
938 | }
|
939 | }
|
940 |
|
941 | NAN_METHOD(BSON::CalculateObjectSize)
|
942 | {
|
943 | Nan::HandleScope scope;
|
944 |
|
945 | if(info.Length() == 1 && !info[0]->IsObject()) return Nan::ThrowError("One argument required - [object]");
|
946 | if(info.Length() == 2 && !info[0]->IsObject() && !info[1]->IsBoolean()) return Nan::ThrowError("Two arguments required - [object, boolean]");
|
947 | if(info.Length() > 3) return Nan::ThrowError("One or two arguments required - [object] or [object, boolean]");
|
948 |
|
949 |
|
950 | BSON *bson = ObjectWrap::Unwrap<BSON>(info.This());
|
951 | bool serializeFunctions = (info.Length() >= 2) && info[1]->BooleanValue();
|
952 | BSONSerializer<CountStream> countSerializer(bson, false, serializeFunctions);
|
953 | countSerializer.SerializeDocument(info[0]);
|
954 |
|
955 |
|
956 | info.GetReturnValue().Set(Nan::New<Uint32>((uint32_t) countSerializer.GetSerializeSize()));
|
957 | }
|
958 |
|
959 | NAN_METHOD(BSON::SerializeWithBufferAndIndex)
|
960 | {
|
961 | Nan::HandleScope scope;
|
962 |
|
963 |
|
964 |
|
965 | if(info.Length() > 5) return Nan::ThrowError("Four or five parameters required [object, boolean, Buffer, int] or [object, boolean, Buffer, int, boolean]");
|
966 | if(info.Length() == 4 && !info[0]->IsObject() && !info[1]->IsBoolean() && !Buffer::HasInstance(info[2]) && !info[3]->IsUint32()) return Nan::ThrowError("Four parameters required [object, boolean, Buffer, int]");
|
967 | if(info.Length() == 5 && !info[0]->IsObject() && !info[1]->IsBoolean() && !Buffer::HasInstance(info[2]) && !info[3]->IsUint32() && !info[4]->IsBoolean()) return Nan::ThrowError("Four parameters required [object, boolean, Buffer, int, boolean]");
|
968 |
|
969 | uint32_t index;
|
970 | size_t object_size;
|
971 |
|
972 | try
|
973 | {
|
974 | BSON *bson = ObjectWrap::Unwrap<BSON>(info.This());
|
975 |
|
976 | Local<Object> obj = info[2]->ToObject();
|
977 | char* data = Buffer::Data(obj);
|
978 | size_t length = Buffer::Length(obj);
|
979 |
|
980 | index = info[3]->Uint32Value();
|
981 | bool checkKeys = info.Length() >= 4 && info[1]->IsBoolean() && info[1]->BooleanValue();
|
982 | bool serializeFunctions = (info.Length() == 5) && info[4]->BooleanValue();
|
983 |
|
984 | BSONSerializer<DataStream> dataSerializer(bson, checkKeys, serializeFunctions, data+index);
|
985 | dataSerializer.SerializeDocument(bson->GetSerializeObject(info[0]));
|
986 | object_size = dataSerializer.GetSerializeSize();
|
987 |
|
988 | if(object_size + index > length) return Nan::ThrowError("Serious error - overflowed buffer!!");
|
989 | }
|
990 | catch(char *exception)
|
991 | {
|
992 | Local<String> error = Nan::New<String>(exception).ToLocalChecked();
|
993 | free(exception);
|
994 | return Nan::ThrowError(error);
|
995 | }
|
996 |
|
997 | info.GetReturnValue().Set(Nan::New<Uint32>((uint32_t) (index + object_size - 1)));
|
998 | }
|
999 |
|
1000 | NAN_METHOD(BSON::BSONDeserializeStream)
|
1001 | {
|
1002 | Nan::HandleScope scope;
|
1003 |
|
1004 |
|
1005 | if(info.Length() < 5) return Nan::ThrowError("Arguments required (Buffer(data), Number(index in data), Number(number of documents to deserialize), Array(results), Number(index in the array), Object(optional))");
|
1006 |
|
1007 |
|
1008 | if(info.Length() >= 5)
|
1009 | {
|
1010 | if(!Buffer::HasInstance(info[0])) return Nan::ThrowError("First argument must be Buffer instance");
|
1011 | if(!info[1]->IsUint32()) return Nan::ThrowError("Second argument must be a positive index number");
|
1012 | if(!info[2]->IsUint32()) return Nan::ThrowError("Third argument must be a positive number of documents to deserialize");
|
1013 | if(!info[3]->IsArray()) return Nan::ThrowError("Fourth argument must be an array the size of documents to deserialize");
|
1014 | if(!info[4]->IsUint32()) return Nan::ThrowError("Sixth argument must be a positive index number");
|
1015 | }
|
1016 |
|
1017 |
|
1018 | if(info.Length() == 6 && !info[5]->IsObject()) return Nan::ThrowError("Fifth argument must be an object with options");
|
1019 |
|
1020 |
|
1021 | Local<Object> obj = info[0]->ToObject();
|
1022 | uint32_t numberOfDocuments = info[2]->Uint32Value();
|
1023 | uint32_t index = info[1]->Uint32Value();
|
1024 | uint32_t resultIndex = info[4]->Uint32Value();
|
1025 | bool promoteLongs = true;
|
1026 |
|
1027 |
|
1028 | if(info.Length() == 6) {
|
1029 | Local<Object> options = info[5]->ToObject();
|
1030 |
|
1031 |
|
1032 | if(options->Has(Nan::New<String>("promoteLongs").ToLocalChecked())) {
|
1033 | promoteLongs = options->Get(Nan::New<String>("promoteLongs").ToLocalChecked())->ToBoolean()->Value();
|
1034 | }
|
1035 | }
|
1036 |
|
1037 |
|
1038 | BSON *bson = ObjectWrap::Unwrap<BSON>(info.This());
|
1039 |
|
1040 |
|
1041 | #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION < 3
|
1042 | Local<Object> buffer = ObjectWrap::Unwrap<Buffer>(obj);
|
1043 | char* data = buffer->data();
|
1044 | size_t length = buffer->length();
|
1045 | #else
|
1046 | char* data = Buffer::Data(obj);
|
1047 | size_t length = Buffer::Length(obj);
|
1048 | #endif
|
1049 |
|
1050 |
|
1051 | Local<Object> documents = info[3]->ToObject();
|
1052 |
|
1053 | BSONDeserializer deserializer(bson, data+index, length-index);
|
1054 | for(uint32_t i = 0; i < numberOfDocuments; i++)
|
1055 | {
|
1056 | try
|
1057 | {
|
1058 | documents->Set(i + resultIndex, deserializer.DeserializeDocument(promoteLongs));
|
1059 | }
|
1060 | catch (char* exception)
|
1061 | {
|
1062 | Local<String> error = Nan::New<String>(exception).ToLocalChecked();
|
1063 | free(exception);
|
1064 | return Nan::ThrowError(error);
|
1065 | }
|
1066 | }
|
1067 |
|
1068 |
|
1069 | info.GetReturnValue().Set(Nan::New<Uint32>((uint32_t) (index + deserializer.GetSerializeSize())));
|
1070 | }
|
1071 |
|
1072 |
|
1073 | extern "C" void init(Local<Object> target)
|
1074 | {
|
1075 | Nan::HandleScope scope;
|
1076 | BSON::Initialize(target);
|
1077 | }
|
1078 |
|
1079 |
|