diff --git a/src/resize.cc b/src/resize.cc index 72e29f69..4157340d 100755 --- a/src/resize.cc +++ b/src/resize.cc @@ -51,7 +51,7 @@ enum class Angle { struct ResizeBaton { std::string fileIn; - void* bufferIn; + char* bufferIn; size_t bufferInLength; std::string iccProfilePath; int limitInputPixels; @@ -125,6 +125,20 @@ struct ResizeBaton { } }; +/* + Delete input char[] buffer and notify V8 of memory deallocation + Used as the callback function for the "postclose" signal +*/ +static void DeleteBuffer(VipsObject *object, std::tuple *buffer) { + char* data = std::get<0>(*buffer); + size_t length = std::get<1>(*buffer); + if (data != NULL) { + delete[] data; + } + NanAdjustExternalMemory(static_cast(-length)); + delete buffer; +} + class ResizeWorker : public NanAsyncWorker { public: @@ -149,18 +163,25 @@ class ResizeWorker : public NanAsyncWorker { // Input ImageType inputImageType = ImageType::UNKNOWN; - VipsImage *image; + VipsImage *image = NULL; if (baton->bufferInLength > 1) { // From buffer inputImageType = DetermineImageType(baton->bufferIn, baton->bufferInLength); + std::tuple *buffer = new std::tuple(baton->bufferIn, baton->bufferInLength); if (inputImageType != ImageType::UNKNOWN) { image = InitImage(inputImageType, baton->bufferIn, baton->bufferInLength, baton->accessMethod); - if (image == NULL) { + if (image != NULL) { + // Listen for "postclose" signal to delete input buffer + g_signal_connect(image, "postclose", G_CALLBACK(DeleteBuffer), buffer); + } else { + // Could not read header data (baton->err).append("Input buffer has corrupt header"); inputImageType = ImageType::UNKNOWN; + DeleteBuffer(NULL, buffer); } } else { (baton->err).append("Input buffer contains unsupported image format"); + DeleteBuffer(NULL, buffer); } } else { // From file @@ -175,7 +196,7 @@ class ResizeWorker : public NanAsyncWorker { (baton->err).append("Input file is of an unsupported image format"); } } - if (inputImageType == ImageType::UNKNOWN) { + if (image == NULL || inputImageType == ImageType::UNKNOWN) { return Error(baton, hook); } vips_object_local(hook, image); @@ -775,11 +796,6 @@ class ResizeWorker : public NanAsyncWorker { void HandleOKCallback () { NanScope(); - // Free input Buffer - if (baton->bufferInLength > 0) { - g_free(baton->bufferIn); - } - Handle argv[3] = { NanNull(), NanNull(), NanNull() }; if (!baton->err.empty()) { // Error @@ -804,6 +820,7 @@ class ResizeWorker : public NanAsyncWorker { if (baton->bufferOutLength > 0) { // Copy data to new Buffer argv[1] = NanNewBufferHandle(static_cast(baton->bufferOut), baton->bufferOutLength); + // bufferOut was allocated via malloc g_free(baton->bufferOut); // Add buffer size to info info->Set(NanNew("size"), NanNew(static_cast(baton->bufferOutLength))); @@ -925,9 +942,11 @@ NAN_METHOD(resize) { Local buffer = options->Get(NanNew("bufferIn"))->ToObject(); // Take a copy of the input Buffer to avoid problems with V8 heap compaction baton->bufferInLength = node::Buffer::Length(buffer); - baton->bufferIn = g_malloc(baton->bufferInLength); + baton->bufferIn = new char[baton->bufferInLength]; memcpy(baton->bufferIn, node::Buffer::Data(buffer), baton->bufferInLength); options->Set(NanNew("bufferIn"), NanNull()); + // Notify V8 GC of memory allocation + NanAdjustExternalMemory(static_cast(baton->bufferInLength)); } // ICC profile to use when input CMYK image has no embedded profile baton->iccProfilePath = *String::Utf8Value(options->Get(NanNew("iccProfilePath"))->ToString());