#include "lmdb-js.h" #include #include static thread_local char* globalUnsafePtr; static thread_local size_t globalUnsafeSize; void setupExportMisc(Local exports) { Local versionObj = Nan::New(); int major, minor, patch; char *str = mdb_version(&major, &minor, &patch); Local context = Nan::GetCurrentContext(); (void)versionObj->Set(context, Nan::New("versionString").ToLocalChecked(), Nan::New(str).ToLocalChecked()); (void)versionObj->Set(context, Nan::New("major").ToLocalChecked(), Nan::New(major)); (void)versionObj->Set(context, Nan::New("minor").ToLocalChecked(), Nan::New(minor)); (void)versionObj->Set(context, Nan::New("patch").ToLocalChecked(), Nan::New(patch)); (void)exports->Set(context, Nan::New("version").ToLocalChecked(), versionObj); Nan::SetMethod(exports, "setGlobalBuffer", setGlobalBuffer); Nan::SetMethod(exports, "lmdbError", lmdbError); //Nan::SetMethod(exports, "getBufferForAddress", getBufferForAddress); Nan::SetMethod(exports, "getAddress", getViewAddress); Nan::SetMethod(exports, "clearKeptObjects", clearKeptObjects); // this is set solely for the purpose of giving a good name to the set of native functions for the profiler since V8 // often just uses the name of the last exported native function: Nan::SetMethod(exports, "lmdbNativeFunctions", lmdbNativeFunctions); } extern "C" EXTERN void freeData(double ref) { delete (char*) (size_t) ref; } extern "C" EXTERN size_t getAddress(char* buffer) { return (size_t) buffer; } extern "C" EXTERN void setGlobalBuffer(char* buffer, size_t bufferSize) { globalUnsafePtr = buffer; globalUnsafeSize = bufferSize; } extern "C" EXTERN void getError(int rc, char* target) { strcpy(target, mdb_strerror(rc)); } void setFlagFromValue(int *flags, int flag, const char *name, bool defaultValue, Local options) { Local context = Nan::GetCurrentContext(); Local opt = options->Get(context, Nan::New(name).ToLocalChecked()).ToLocalChecked(); #if NODE_VERSION_AT_LEAST(12,0,0) if (opt->IsBoolean() ? opt->BooleanValue(Isolate::GetCurrent()) : defaultValue) { #else if (opt->IsBoolean() ? opt->BooleanValue(context).FromJust() : defaultValue) { #endif *flags |= flag; } } LmdbKeyType keyTypeFromOptions(const Local &val, LmdbKeyType defaultKeyType) { if (!val->IsObject()) { return defaultKeyType; } auto obj = Local::Cast(val); LmdbKeyType keyType = defaultKeyType; int keyIsUint32 = 0; int keyIsBuffer = 0; int keyIsString = 0; setFlagFromValue(&keyIsUint32, 1, "keyIsUint32", false, obj); setFlagFromValue(&keyIsString, 1, "keyIsString", false, obj); setFlagFromValue(&keyIsBuffer, 1, "keyIsBuffer", false, obj); const char *keySpecificationErrorText = "You can't specify multiple key types at once. Either set keyIsUint32, or keyIsBuffer or keyIsString (default)."; if (keyIsUint32) { keyType = LmdbKeyType::Uint32Key; if (keyIsBuffer || keyIsString) { Nan::ThrowError(keySpecificationErrorText); return LmdbKeyType::InvalidKey; } } else if (keyIsBuffer) { keyType = LmdbKeyType::BinaryKey; if (keyIsUint32 || keyIsString) { Nan::ThrowError(keySpecificationErrorText); return LmdbKeyType::InvalidKey; } } else if (keyIsString) { keyType = LmdbKeyType::StringKey; } return keyType; } Local valToStringUnsafe(MDB_val &data) { auto resource = new CustomExternalOneByteStringResource(&data); auto str = Nan::New(resource); return str.ToLocalChecked(); } Local valToUtf8(MDB_val &data) { //const uint8_t *buffer = (const uint8_t*)(data.mv_data); //Isolate *isolate = Isolate::GetCurrent(); //auto str = v8::String::NewFromOneByte(isolate, buffer, v8::NewStringType::kNormal, data.mv_size); const char *buffer = (const char*)(data.mv_data); auto str = Nan::New(buffer, data.mv_size); return str.ToLocalChecked(); } Local valToString(MDB_val &data) { // UTF-16 buffer const uint16_t *buffer = reinterpret_cast(data.mv_data); // Number of UTF-16 code points size_t n = data.mv_size / sizeof(uint16_t); // Check zero termination if (n < 1 || buffer[n - 1] != 0) { Nan::ThrowError("Invalid zero-terminated UTF-16 string"); return Nan::Undefined(); } size_t length = n - 1; auto str = Nan::New(buffer, length); return str.ToLocalChecked(); } bool valToBinaryFast(MDB_val &data, DbiWrap* dw) { Compression* compression = dw->compression; if (compression) { if (data.mv_data == compression->decompressTarget) { // already decompressed to the target, nothing more to do } else { if (data.mv_size > compression->decompressSize) { return false; } // copy into the buffer target memcpy(compression->decompressTarget, data.mv_data, data.mv_size); } } else { if (data.mv_size > globalUnsafeSize) { // TODO: Provide a direct reference if for really large blocks, but we do that we need to detach that in the next turn /* if(data.mv_size > 64000) { dw->SetUnsafeBuffer(data.mv_data, data.mv_size); return Nan::New(data.mv_size); }*/ return false; } memcpy(globalUnsafePtr, data.mv_data, data.mv_size); } return true; } Local valToBinaryUnsafe(MDB_val &data, DbiWrap* dw) { valToBinaryFast(data, dw); return Nan::New(data.mv_size); } bool getVersionAndUncompress(MDB_val &data, DbiWrap* dw) { //fprintf(stdout, "uncompressing %u\n", compressionThreshold); unsigned char* charData = (unsigned char*) data.mv_data; if (dw->hasVersions) { *((double*) (dw->ew->keyBuffer + 16)) = *((double*) charData); // fprintf(stderr, "getVersion %u\n", lastVersion); charData = charData + 8; data.mv_data = charData; data.mv_size -= 8; } if (data.mv_size == 0) { return true;// successFunc(data); } unsigned char statusByte = dw->compression ? charData[0] : 0; //fprintf(stdout, "uncompressing status %X\n", statusByte); if (statusByte >= 250) { bool isValid; dw->compression->decompress(data, isValid, !dw->getFast); if (!isValid) return false; //return Nan::Null(); } return true; } NAN_METHOD(lmdbError) { throwLmdbError(Nan::To(info[0]).ToLocalChecked()->Value()); } NAN_METHOD(setGlobalBuffer) { globalUnsafePtr = node::Buffer::Data(info[0]); globalUnsafeSize = node::Buffer::Length(info[0]); } /*NAN_METHOD(getBufferForAddress) { char* address = (char*) (size_t) Nan::To(info[0]).ToLocalChecked()->Value(); std::unique_ptr backing = v8::ArrayBuffer::NewBackingStore( address, 0x100000000, [](void*, size_t, void*){}, nullptr); auto array_buffer = v8::ArrayBuffer::New(Isolate::GetCurrent(), std::move(backing)); info.GetReturnValue().Set(array_buffer); }*/ NAN_METHOD(getViewAddress) { int length = node::Buffer::Length(info[0]); void* address = length > 0 ? node::Buffer::Data(info[0]) : nullptr; info.GetReturnValue().Set(Nan::New((size_t) address)); } NAN_METHOD(clearKeptObjects) { #if NODE_VERSION_AT_LEAST(12,0,0) Isolate::GetCurrent()->ClearKeptObjects(); #endif } NAN_METHOD(lmdbNativeFunctions) { // no-op, just doing this to give a label to the native functions } void throwLmdbError(int rc) { auto err = Nan::Error(mdb_strerror(rc)); (void)err.As()->Set(Nan::GetCurrentContext(), Nan::New("code").ToLocalChecked(), Nan::New(rc)); return Nan::ThrowError(err); } void consoleLog(const char *msg) { Local str = Nan::New("console.log('").ToLocalChecked(); //str = String::Concat(str, Nan::New(msg).ToLocalChecked()); //str = String::Concat(str, Nan::New("');").ToLocalChecked()); Local