changeset 6182:0d540324c14f attach-custom-data

integration mainline->attach-custom-data
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 12 Jun 2025 12:29:39 +0200
parents 8bd3a683778d (current diff) 39efb66b8322 (diff)
children 7f985407c885
files OrthancServer/CMakeLists.txt OrthancServer/Plugins/Engine/OrthancPlugins.cpp OrthancServer/Plugins/Engine/OrthancPlugins.h OrthancServer/Sources/ServerContext.cpp
diffstat 9 files changed, 640 insertions(+), 271 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancServer/CMakeLists.txt	Wed Jun 11 20:51:30 2025 +0200
+++ b/OrthancServer/CMakeLists.txt	Thu Jun 12 12:29:39 2025 +0200
@@ -203,6 +203,8 @@
     ${CMAKE_SOURCE_DIR}/Plugins/Engine/OrthancPluginDatabaseV3.cpp
     ${CMAKE_SOURCE_DIR}/Plugins/Engine/OrthancPluginDatabaseV4.cpp
     ${CMAKE_SOURCE_DIR}/Plugins/Engine/OrthancPlugins.cpp
+    ${CMAKE_SOURCE_DIR}/Plugins/Engine/PluginMemoryBuffer32.cpp
+    ${CMAKE_SOURCE_DIR}/Plugins/Engine/PluginMemoryBuffer64.cpp
     ${CMAKE_SOURCE_DIR}/Plugins/Engine/PluginsEnumerations.cpp
     ${CMAKE_SOURCE_DIR}/Plugins/Engine/PluginsErrorDictionary.cpp
     ${CMAKE_SOURCE_DIR}/Plugins/Engine/PluginsJob.cpp
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Wed Jun 11 20:51:30 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Thu Jun 12 12:29:39 2025 +0200
@@ -65,6 +65,7 @@
 #include "OrthancPluginDatabase.h"
 #include "OrthancPluginDatabaseV3.h"
 #include "OrthancPluginDatabaseV4.h"
+#include "PluginMemoryBuffer32.h"
 #include "PluginsEnumerations.h"
 #include "PluginsJob.h"
 
@@ -536,78 +537,45 @@
   };
   
 
-  static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target,
+  static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer* target,
                                  const void* data,
                                  size_t size)
   {
-    if (static_cast<uint32_t>(size) != size)
-    {
-      throw OrthancException(ErrorCode_NotEnoughMemory, ERROR_MESSAGE_64BIT);
-    }
-
-    target.size = size;
-
-    if (size == 0)
-    {
-      target.data = NULL;
-    }
-    else
-    {
-      target.data = malloc(size);
-      if (target.data != NULL)
-      {
-        memcpy(target.data, data, size);
-      }
-      else
-      {
-        throw OrthancException(ErrorCode_NotEnoughMemory);
-      }
-    }
+    PluginMemoryBuffer32 buffer;
+    buffer.Assign(data, size);
+    buffer.Release(target);
   }
 
 
-  static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target,
+  static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer* target,
                                  const std::string& str)
   {
-    if (str.size() == 0)
-    {
-      target.size = 0;
-      target.data = NULL;
-    }
-    else
-    {
-      CopyToMemoryBuffer(target, str.c_str(), str.size());
-    }
+    PluginMemoryBuffer32 buffer;
+    buffer.Assign(str);
+    buffer.Release(target);
   }
 
 
   static char* CopyString(const std::string& str)
   {
-    if (static_cast<uint32_t>(str.size()) != str.size())
-    {
-      throw OrthancException(ErrorCode_NotEnoughMemory, ERROR_MESSAGE_64BIT);
-    }
-
     char *result = reinterpret_cast<char*>(malloc(str.size() + 1));
     if (result == NULL)
     {
       throw OrthancException(ErrorCode_NotEnoughMemory);
     }
 
-    if (str.size() == 0)
-    {
-      result[0] = '\0';
-    }
-    else
-    {
-      memcpy(result, &str[0], str.size() + 1);
-    }
+    if (!str.empty())
+    {
+      memcpy(result, str.c_str(), str.size());
+    }
+
+    result[str.size() - 1] = '\0';  // Add the null terminator of the string
 
     return result;
   }
 
 
-  static void CopyDictionary(OrthancPluginMemoryBuffer& target,
+  static void CopyDictionary(PluginMemoryBuffer32& target,
                              const std::map<std::string, std::string>& dictionary)
   {
     Json::Value json = Json::objectValue;
@@ -618,58 +586,12 @@
       json[it->first] = it->second;
     }
         
-    std::string s = json.toStyledString();
-    CopyToMemoryBuffer(target, s);
+    target.Assign(json.toStyledString());
   }
 
 
   namespace
   {
-    class MemoryBufferRaii : public boost::noncopyable
-    {
-    private:
-      OrthancPluginMemoryBuffer  buffer_;
-
-    public:
-      MemoryBufferRaii()
-      {
-        buffer_.size = 0;
-        buffer_.data = NULL;
-      }
-
-      ~MemoryBufferRaii()
-      {
-        if (buffer_.size != 0)
-        {
-          free(buffer_.data);
-        }
-      }
-
-      OrthancPluginMemoryBuffer* GetObject()
-      {
-        return &buffer_;
-      }
-
-      void ToString(std::string& target) const
-      {
-        if ((buffer_.data == NULL && buffer_.size != 0) ||
-            (buffer_.data != NULL && buffer_.size == 0))
-        {
-          throw OrthancException(ErrorCode_Plugin);
-        }
-        else
-        {
-          target.resize(buffer_.size);
-        
-          if (buffer_.size != 0)
-          {
-            memcpy(&target[0], buffer_.data, buffer_.size);
-          }
-        }
-      }
-    };
-
-
     static IMemoryBuffer* GetRangeFromWhole(std::unique_ptr<MallocMemoryBuffer>& whole,
                                             uint64_t start /* inclusive */,
                                             uint64_t end /* exclusive */)
@@ -863,30 +785,27 @@
         }
         else
         {
+          std::unique_ptr<PluginMemoryBuffer64> buffer(new PluginMemoryBuffer64);
+
           if (start > end)
           {
             throw OrthancException(ErrorCode_BadRange);
           }
           else if (start == end)
           {
-            return new StringMemoryBuffer;
+            return buffer.release();
           }
           else
           {
-            std::string range;
-            range.resize(end - start);
-            assert(!range.empty());
-
-            OrthancPluginMemoryBuffer64 buffer;
-            buffer.data = &range[0];
-            buffer.size = static_cast<uint64_t>(range.size());
+            buffer->Resize(end - start);
+            assert(buffer->GetSize() > 0);
 
             OrthancPluginErrorCode error =
-              readRange_(&buffer, uuid.c_str(), Plugins::Convert(type), start);
+              readRange_(buffer->GetObject(), uuid.c_str(), Plugins::Convert(type), start);
 
             if (error == OrthancPluginErrorCode_Success)
             {
-              return StringMemoryBuffer::CreateFromSwap(range);
+              return buffer.release();
             }
             else
             {
@@ -944,7 +863,7 @@
                           CompressionType compression,
                           const DicomInstanceToStore* dicomInstance /* can be NULL if not a DICOM instance */) ORTHANC_OVERRIDE
       {
-        MemoryBufferRaii customDataBuffer;
+        PluginMemoryBuffer32 customDataBuffer;
         OrthancPluginErrorCode error;
 
         if (dicomInstance != NULL)
@@ -965,7 +884,7 @@
         }
         else
         {
-          customDataBuffer.ToString(customData);
+          customDataBuffer.MoveToString(customData);
         }
       }
 
@@ -1954,7 +1873,7 @@
       }
     }
 
-    void GetDicomQuery(OrthancPluginMemoryBuffer& target) const
+    void GetDicomQuery(OrthancPluginMemoryBuffer* target) const
     {
       if (currentQuery_ == NULL)
       {
@@ -2855,32 +2774,22 @@
   }
 
 
-  OrthancPluginReceivedInstanceAction OrthancPlugins::ApplyReceivedInstanceCallbacks(
-    MallocMemoryBuffer& modified,
-    const void* receivedDicom,
-    size_t receivedDicomSize,
-    RequestOrigin origin)
+  OrthancPluginReceivedInstanceAction OrthancPlugins::ApplyReceivedInstanceCallbacks(PluginMemoryBuffer64& modified,
+                                                                                     const void* receivedDicom,
+                                                                                     size_t receivedDicomSize,
+                                                                                     RequestOrigin origin)
   {
     boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_);
 
+    modified.Clear();
+
     if (pimpl_->receivedInstanceCallback_ == NULL)
     {
       return OrthancPluginReceivedInstanceAction_KeepAsIs;
     }
     else
     {
-      OrthancPluginReceivedInstanceAction action;
-      
-      {
-        OrthancPluginMemoryBuffer64 buffer;
-        buffer.size = 0;
-        buffer.data = NULL;
-
-        action = (*pimpl_->receivedInstanceCallback_) (&buffer, receivedDicom, receivedDicomSize, Plugins::Convert(origin));
-        modified.Assign(buffer.data, buffer.size, ::free);
-      }
-
-      return action;
+      return (*pimpl_->receivedInstanceCallback_) (modified.GetObject(), receivedDicom, receivedDicomSize, Plugins::Convert(origin));
     }
   }
 
@@ -3344,7 +3253,7 @@
       lock.GetContext().ReadDicom(dicom, p.instanceId);
     }
 
-    CopyToMemoryBuffer(*p.target, dicom);
+    CopyToMemoryBuffer(p.target, dicom);
   }
 
   static void ThrowOnHttpError(HttpStatus httpStatus)
@@ -3394,7 +3303,7 @@
     std::string result;
 
     ThrowOnHttpError(IHttpHandler::SimpleGet(result, NULL, *handler, RequestOrigin_Plugins, p.uri, httpHeaders));
-    CopyToMemoryBuffer(*p.target, result);
+    CopyToMemoryBuffer(p.target, result);
   }
 
 
@@ -3425,7 +3334,7 @@
     std::string result;
 
     ThrowOnHttpError(IHttpHandler::SimpleGet(result, NULL, *handler, RequestOrigin_Plugins, p.uri, headers));
-    CopyToMemoryBuffer(*p.target, result);
+    CopyToMemoryBuffer(p.target, result);
   }
 
 
@@ -3455,7 +3364,7 @@
                                  p.body, p.bodySize, httpHeaders) :
         IHttpHandler::SimplePut(result, NULL, *handler, RequestOrigin_Plugins, p.uri,
                                 p.body, p.bodySize, httpHeaders)));
-    CopyToMemoryBuffer(*p.target, result);
+    CopyToMemoryBuffer(p.target, result);
   }
 
 
@@ -3737,7 +3646,7 @@
 
         case OrthancPluginCompressionType_None:
         {
-          CopyToMemoryBuffer(*p.target, p.source, p.size);
+          CopyToMemoryBuffer(p.target, p.source, p.size);
           return;
         }
 
@@ -3755,7 +3664,7 @@
       }
     }
 
-    CopyToMemoryBuffer(*p.target, result);
+    CopyToMemoryBuffer(p.target, result);
   }
 
 
@@ -3816,7 +3725,7 @@
         MimeType mime;
         std::string frame;
         instance.GetParsedDicomFile().GetRawFrame(frame, mime, p.frameIndex);
-        CopyToMemoryBuffer(*p.targetBuffer, frame);
+        CopyToMemoryBuffer(p.targetBuffer, frame);
         return;
       }
         
@@ -3846,7 +3755,7 @@
 
         p.targetBuffer->data = NULL;
         p.targetBuffer->size = 0;
-        CopyToMemoryBuffer(*p.targetBuffer, instance.GetBufferData(), instance.GetBufferSize());
+        CopyToMemoryBuffer(p.targetBuffer, instance.GetBufferData(), instance.GetBufferSize());
         return;
       }
 
@@ -3955,7 +3864,7 @@
         throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
 
-    CopyToMemoryBuffer(*p.target, compressed);
+    CopyToMemoryBuffer(p.target, compressed);
   }
 
 
@@ -4052,29 +3961,29 @@
     }
 
     // Copy the HTTP headers of the answer, if the plugin requested them
+    PluginMemoryBuffer32 tmpHeaders;
     if (answerHeaders != NULL)
     {
-      CopyDictionary(*answerHeaders, headers);
+      CopyDictionary(tmpHeaders, headers);
     }
 
     // Copy the body of the answer if it makes sense
-    if (client.GetMethod() != HttpMethod_Delete)
-    {
-      try
-      {
-        if (answerBody != NULL)
-        {
-          CopyToMemoryBuffer(*answerBody, body);
-        }
-      }
-      catch (OrthancException&)
-      {
-        if (answerHeaders != NULL)
-        {
-          free(answerHeaders->data);
-        }
-        throw;
-      }
+    PluginMemoryBuffer32 tmpBody;
+    if (client.GetMethod() != HttpMethod_Delete &&
+        answerBody != NULL)
+    {
+      tmpBody.Assign(body);
+    }
+
+    // All the memory has been allocated at this point, so we can safely release the buffers
+    if (answerHeaders != NULL)
+    {
+      tmpHeaders.Release(answerHeaders);
+    }
+
+    if (answerBody != NULL)
+    {
+      tmpBody.Release(answerBody);
     }
   }
 
@@ -4273,25 +4182,28 @@
 
     *p.httpStatus = static_cast<uint16_t>(status);
 
+    PluginMemoryBuffer32 tmpHeaders;
     if (p.answerHeaders != NULL)
     {
-      CopyDictionary(*p.answerHeaders, answerHeaders);
-    }
-
-    try
-    {
-      if (p.answerBody != NULL)
-      {
-        CopyToMemoryBuffer(*p.answerBody, answerBody);
-      }
-    }
-    catch (OrthancException&)
-    {
-      if (p.answerHeaders != NULL)
-      {
-        free(p.answerHeaders->data);
-      }
-      throw;
+      CopyDictionary(tmpHeaders, answerHeaders);
+    }
+
+    PluginMemoryBuffer32 tmpBody;
+    if (p.method != OrthancPluginHttpMethod_Delete &&
+        p.answerBody != NULL)
+    {
+      tmpBody.Assign(answerBody);
+    }
+
+    // All the memory has been allocated at this point, so we can safely release the buffers
+    if (p.answerHeaders != NULL)
+    {
+      tmpHeaders.Release(p.answerHeaders);
+    }
+
+    if (p.answerBody != NULL)
+    {
+      tmpBody.Release(p.answerBody);
     }
   }
 
@@ -4358,29 +4270,29 @@
     }
 
     // Copy the HTTP headers of the answer, if the plugin requested them
+    PluginMemoryBuffer32 tmpHeaders;
     if (p.answerHeaders != NULL)
     {
-      CopyDictionary(*p.answerHeaders, headers);
+      CopyDictionary(tmpHeaders, headers);
     }
 
     // Copy the body of the answer if it makes sense
-    if (p.method != OrthancPluginHttpMethod_Delete)
-    {
-      try
-      {
-        if (p.answerBody != NULL)
-        {
-          CopyToMemoryBuffer(*p.answerBody, body);
-        }
-      }
-      catch (OrthancException&)
-      {
-        if (p.answerHeaders != NULL)
-        {
-          free(p.answerHeaders->data);
-        }
-        throw;
-      }
+    PluginMemoryBuffer32 tmpBody;
+    if (p.method != OrthancPluginHttpMethod_Delete &&
+        p.answerBody != NULL)
+    {
+      tmpBody.Assign(body);
+    }
+
+    // All the memory has been allocated at this point, so we can safely release the buffers
+    if (p.answerHeaders != NULL)
+    {
+      tmpHeaders.Release(p.answerHeaders);
+    }
+
+    if (p.answerBody != NULL)
+    {
+      tmpBody.Release(p.answerBody);
     }
   }
 
@@ -4521,7 +4433,7 @@
       file->SaveToMemoryBuffer(dicom);
     }
 
-    CopyToMemoryBuffer(*parameters.target, dicom);
+    CopyToMemoryBuffer(parameters.target, dicom);
   }
 
 
@@ -4680,8 +4592,8 @@
 
       ServerContext::StoreResult result = lock.GetContext().AdoptAttachment(resultPublicId, *dicom, StoreInstanceMode_Default, adoptedFile);
 
-      CopyToMemoryBuffer(*parameters.attachmentUuid, adoptedFile.GetUuid());
-      CopyToMemoryBuffer(*parameters.createdResourceId, resultPublicId);
+      CopyToMemoryBuffer(parameters.attachmentUuid, adoptedFile.GetUuid());
+      CopyToMemoryBuffer(parameters.createdResourceId, resultPublicId);
       *(parameters.storeStatus) = Plugins::Convert(result.GetStatus());
     }
   }
@@ -4705,7 +4617,7 @@
     
     if (lock.GetContext().GetIndex().GetAttachment(fileInfo, revision, parameters.attachmentUuid))
     {
-      CopyToMemoryBuffer(*parameters.customData, fileInfo.GetCustomData());
+      CopyToMemoryBuffer(parameters.customData, fileInfo.GetCustomData());
     }
     else
     {
@@ -4758,7 +4670,7 @@
 
     if (lock.GetContext().GetIndex().GetKeyValue(value, parameters.storeId, parameters.key))
     {
-      CopyToMemoryBuffer(*parameters.target, value);
+      CopyToMemoryBuffer(parameters.target, value);
       *parameters.found = true;
     }
     else
@@ -4804,7 +4716,7 @@
 
     if (lock.GetContext().GetIndex().DequeueValue(value, parameters.queueId, Plugins::Convert(parameters.origin)))
     {
-      CopyToMemoryBuffer(*parameters.target, value);
+      CopyToMemoryBuffer(parameters.target, value);
       *parameters.found = true;
     }
     else
@@ -5270,7 +5182,7 @@
 
         std::string content;
         SystemToolbox::ReadFile(content, p.path);
-        CopyToMemoryBuffer(*p.target, content);
+        CopyToMemoryBuffer(p.target, content);
 
         return true;
       }
@@ -5446,7 +5358,7 @@
       {
         const _OrthancPluginWorklistQueryOperation& p =
           *reinterpret_cast<const _OrthancPluginWorklistQueryOperation*>(parameters);
-        reinterpret_cast<const WorklistHandler*>(p.query)->GetDicomQuery(*p.target);
+        reinterpret_cast<const WorklistHandler*>(p.query)->GetDicomQuery(p.target);
         return true;
       }
 
@@ -5829,20 +5741,10 @@
         const _OrthancPluginCreateMemoryBuffer& p =
           *reinterpret_cast<const _OrthancPluginCreateMemoryBuffer*>(parameters);
 
-        p.target->data = NULL;
-        p.target->size = 0;
-        
-        if (p.size != 0)
-        {
-          p.target->data = malloc(p.size);
-          if (p.target->data == NULL)
-          {
-            throw OrthancException(ErrorCode_NotEnoughMemory);
-          }
-
-          p.target->size = p.size;
-        }          
-        
+        PluginMemoryBuffer32 buffer;
+        buffer.Resize(p.size);
+        buffer.Release(p.target);
+
         return true;
       }
 
@@ -5851,19 +5753,9 @@
         const _OrthancPluginCreateMemoryBuffer64& p =
           *reinterpret_cast<const _OrthancPluginCreateMemoryBuffer64*>(parameters);
 
-        p.target->data = NULL;
-        p.target->size = 0;
-        
-        if (p.size != 0)
-        {
-          p.target->data = malloc(p.size);
-          if (p.target->data == NULL)
-          {
-            throw OrthancException(ErrorCode_NotEnoughMemory);
-          }
-
-          p.target->size = p.size;
-        }          
+        PluginMemoryBuffer64 buffer;
+        buffer.Resize(p.size);
+        buffer.Release(p.target);
 
         return true;
       }
@@ -5966,7 +5858,7 @@
       {
         const _OrthancPluginKeysValuesIteratorGetValue& p = *reinterpret_cast<const _OrthancPluginKeysValuesIteratorGetValue*>(parameters);
         StatelessDatabaseOperations::KeysValuesIterator& iterator = *reinterpret_cast<StatelessDatabaseOperations::KeysValuesIterator*>(p.iterator);
-        CopyToMemoryBuffer(*p.target, iterator.GetValue());
+        CopyToMemoryBuffer(p.target, iterator.GetValue());
         return true;
       }
 
@@ -6910,13 +6802,13 @@
            transcoder = pimpl_->transcoderCallbacks_.begin();
          transcoder != pimpl_->transcoderCallbacks_.end(); ++transcoder)
     {
-      MemoryBufferRaii a;
+      PluginMemoryBuffer32 a;
 
       if ((*transcoder) (a.GetObject(), buffer, size, uids.empty() ? NULL : &uids[0],
                          static_cast<uint32_t>(uids.size()), allowNewSopInstanceUid) ==
           OrthancPluginErrorCode_Success)
       {
-        a.ToString(target);
+        a.MoveToString(target);
         return true;
       }
     }
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.h	Wed Jun 11 20:51:30 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPlugins.h	Thu Jun 12 12:29:39 2025 +0200
@@ -56,6 +56,7 @@
 #include "../../Sources/IDicomImageDecoder.h"
 #include "../../Sources/IServerListener.h"
 #include "../../Sources/ServerJobs/IStorageCommitmentFactory.h"
+#include "PluginMemoryBuffer64.h"
 #include "PluginsManager.h"
 
 #include <list>
@@ -307,7 +308,7 @@
                                               const DicomInstanceToStore& instance,
                                               const Json::Value& simplified) ORTHANC_OVERRIDE;
 
-    OrthancPluginReceivedInstanceAction ApplyReceivedInstanceCallbacks(MallocMemoryBuffer& modified,
+    OrthancPluginReceivedInstanceAction ApplyReceivedInstanceCallbacks(PluginMemoryBuffer64& modified,
                                                                        const void* receivedDicomBuffer,
                                                                        size_t receivedDicomBufferSize,
                                                                        RequestOrigin origin);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/Plugins/Engine/PluginMemoryBuffer32.cpp	Thu Jun 12 12:29:39 2025 +0200
@@ -0,0 +1,200 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2023 Osimis S.A., Belgium
+ * Copyright (C) 2024-2025 Orthanc Team SRL, Belgium
+ * Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../../Sources/PrecompiledHeadersServer.h"
+#include "PluginMemoryBuffer32.h"
+
+#include "../../../OrthancFramework/Sources/OrthancException.h"
+#include "../../../OrthancFramework/Sources/Toolbox.h"
+
+#define ERROR_MESSAGE_64BIT "A 64bit version of the Orthanc SDK is necessary to use buffers > 4GB, but is currently not available"
+
+
+namespace Orthanc
+{
+  void PluginMemoryBuffer32::Clear()
+  {
+    if (buffer_.size != 0)
+    {
+      ::free(buffer_.data);
+    }
+
+    buffer_.data = NULL;
+    buffer_.size = 0;
+  }
+
+
+  void PluginMemoryBuffer32::SanityCheck() const
+  {
+    if ((buffer_.data == NULL && buffer_.size != 0) ||
+        (buffer_.data != NULL && buffer_.size == 0))
+    {
+      throw OrthancException(ErrorCode_Plugin);
+    }
+  }
+
+
+  PluginMemoryBuffer32::PluginMemoryBuffer32()
+  {
+    buffer_.size = 0;
+    buffer_.data = NULL;
+  }
+
+
+  void PluginMemoryBuffer32::MoveToString(std::string& target)
+  {
+    SanityCheck();
+
+    target.resize(buffer_.size);
+
+    if (buffer_.size != 0)
+    {
+      memcpy(&target[0], buffer_.data, buffer_.size);
+    }
+
+    Clear();
+  }
+
+
+  const void* PluginMemoryBuffer32::GetData() const
+  {
+    SanityCheck();
+
+    if (buffer_.size == 0)
+    {
+      return NULL;
+    }
+    else
+    {
+      return buffer_.data;
+    }
+  }
+
+
+  size_t PluginMemoryBuffer32::GetSize() const
+  {
+    SanityCheck();
+    return buffer_.size;
+  }
+
+
+  void PluginMemoryBuffer32::Release(OrthancPluginMemoryBuffer* target)
+  {
+    SanityCheck();
+
+    if (target == NULL)
+    {
+      throw OrthancException(ErrorCode_NullPointer);
+    }
+
+    target->data = buffer_.data;
+    target->size = buffer_.size;
+
+    buffer_.data = NULL;
+    buffer_.size = 0;
+  }
+
+
+  void PluginMemoryBuffer32::Release(OrthancPluginMemoryBuffer64* target)
+  {
+    SanityCheck();
+
+    if (target == NULL)
+    {
+      throw OrthancException(ErrorCode_NullPointer);
+    }
+
+    target->data = buffer_.data;
+    target->size = buffer_.size;
+
+    buffer_.data = NULL;
+    buffer_.size = 0;
+  }
+
+
+  void PluginMemoryBuffer32::Resize(size_t size)
+  {
+    if (static_cast<size_t>(static_cast<uint32_t>(size)) != size)
+    {
+      throw OrthancException(ErrorCode_NotEnoughMemory, ERROR_MESSAGE_64BIT);
+    }
+
+    if (size != buffer_.size)
+    {
+      Clear();
+
+      if (size == 0)
+      {
+        buffer_.data = NULL;
+      }
+      else
+      {
+        buffer_.data = ::malloc(size);
+
+        if (buffer_.data == NULL)
+        {
+          throw OrthancException(ErrorCode_NotEnoughMemory);
+        }
+      }
+
+      buffer_.size = size;
+    }
+  }
+
+
+  void PluginMemoryBuffer32::Assign(const void* data,
+                                    size_t size)
+  {
+    Resize(size);
+
+    if (size != 0)
+    {
+      memcpy(buffer_.data, data, size);
+    }
+  }
+
+
+  void PluginMemoryBuffer32::Assign(const std::string& data)
+  {
+    if (data.empty())
+    {
+      Assign(NULL, 0);
+    }
+    else
+    {
+      Assign(data.c_str(), data.size());
+    }
+  }
+
+
+  void PluginMemoryBuffer32::ToJsonObject(Json::Value& target) const
+  {
+    SanityCheck();
+
+    if (!Toolbox::ReadJson(target, buffer_.data, buffer_.size) ||
+        target.type() != Json::objectValue)
+    {
+      throw OrthancException(ErrorCode_Plugin, "The plugin has not provided a valid JSON object");
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/Plugins/Engine/PluginMemoryBuffer32.h	Thu Jun 12 12:29:39 2025 +0200
@@ -0,0 +1,78 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2023 Osimis S.A., Belgium
+ * Copyright (C) 2024-2025 Orthanc Team SRL, Belgium
+ * Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#if ORTHANC_ENABLE_PLUGINS != 1
+#  error The plugin support is disabled
+#endif
+
+#include "../../../OrthancFramework/Sources/MallocMemoryBuffer.h"
+#include "../Include/orthanc/OrthancCPlugin.h"
+
+#include <json/value.h>
+
+namespace Orthanc
+{
+  class PluginMemoryBuffer32 : public IMemoryBuffer
+  {
+  private:
+    OrthancPluginMemoryBuffer  buffer_;
+
+    void SanityCheck() const;
+
+  public:
+    PluginMemoryBuffer32();
+
+    virtual ~PluginMemoryBuffer32()
+    {
+      Clear();
+    }
+
+    virtual void MoveToString(std::string& target) ORTHANC_OVERRIDE;
+
+    virtual const void* GetData() const ORTHANC_OVERRIDE;
+
+    virtual size_t GetSize() const ORTHANC_OVERRIDE;
+
+    OrthancPluginMemoryBuffer* GetObject()
+    {
+      return &buffer_;
+    }
+
+    void Release(OrthancPluginMemoryBuffer* target);
+
+    void Release(OrthancPluginMemoryBuffer64* target);
+
+    void Clear();
+
+    void Resize(size_t size);
+
+    void Assign(const void* data,
+                size_t size);
+
+    void Assign(const std::string& data);
+
+    void ToJsonObject(Json::Value& target) const;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/Plugins/Engine/PluginMemoryBuffer64.cpp	Thu Jun 12 12:29:39 2025 +0200
@@ -0,0 +1,163 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2023 Osimis S.A., Belgium
+ * Copyright (C) 2024-2025 Orthanc Team SRL, Belgium
+ * Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../../Sources/PrecompiledHeadersServer.h"
+#include "PluginMemoryBuffer64.h"
+
+#include "../../../OrthancFramework/Sources/OrthancException.h"
+
+
+namespace Orthanc
+{
+  void PluginMemoryBuffer64::Clear()
+  {
+    if (buffer_.size != 0)
+    {
+      ::free(buffer_.data);
+    }
+
+    buffer_.data = NULL;
+    buffer_.size = 0;
+  }
+
+
+  void PluginMemoryBuffer64::SanityCheck() const
+  {
+    if ((buffer_.data == NULL && buffer_.size != 0) ||
+        (buffer_.data != NULL && buffer_.size == 0))
+    {
+      throw OrthancException(ErrorCode_Plugin);
+    }
+  }
+
+
+  PluginMemoryBuffer64::PluginMemoryBuffer64()
+  {
+    buffer_.size = 0;
+    buffer_.data = NULL;
+  }
+
+
+  void PluginMemoryBuffer64::MoveToString(std::string& target)
+  {
+    SanityCheck();
+
+    target.resize(buffer_.size);
+
+    if (buffer_.size != 0)
+    {
+      memcpy(&target[0], buffer_.data, buffer_.size);
+    }
+
+    Clear();
+  }
+
+
+  const void* PluginMemoryBuffer64::GetData() const
+  {
+    SanityCheck();
+
+    if (buffer_.size == 0)
+    {
+      return NULL;
+    }
+    else
+    {
+      return buffer_.data;
+    }
+  }
+
+
+  size_t PluginMemoryBuffer64::GetSize() const
+  {
+    SanityCheck();
+    return buffer_.size;
+  }
+
+
+  void PluginMemoryBuffer64::Release(OrthancPluginMemoryBuffer64* target)
+  {
+    SanityCheck();
+
+    if (target == NULL)
+    {
+      throw OrthancException(ErrorCode_NullPointer);
+    }
+
+    target->data = buffer_.data;
+    target->size = buffer_.size;
+
+    buffer_.data = NULL;
+    buffer_.size = 0;
+  }
+
+
+  void PluginMemoryBuffer64::Resize(size_t size)
+  {
+    if (size != buffer_.size)
+    {
+      Clear();
+
+      if (size == 0)
+      {
+        buffer_.data = NULL;
+      }
+      else
+      {
+        buffer_.data = ::malloc(size);
+
+        if (buffer_.data == NULL)
+        {
+          throw OrthancException(ErrorCode_NotEnoughMemory);
+        }
+      }
+
+      buffer_.size = size;
+    }
+  }
+
+
+  void PluginMemoryBuffer64::Assign(const void* data,
+                                    size_t size)
+  {
+    Resize(size);
+
+    if (size != 0)
+    {
+      memcpy(buffer_.data, data, size);
+    }
+  }
+
+
+  void PluginMemoryBuffer64::Assign(const std::string& data)
+  {
+    if (data.empty())
+    {
+      Assign(NULL, 0);
+    }
+    else
+    {
+      Assign(data.c_str(), data.size());
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/Plugins/Engine/PluginMemoryBuffer64.h	Thu Jun 12 12:29:39 2025 +0200
@@ -0,0 +1,74 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2023 Osimis S.A., Belgium
+ * Copyright (C) 2024-2025 Orthanc Team SRL, Belgium
+ * Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#if ORTHANC_ENABLE_PLUGINS != 1
+#  error The plugin support is disabled
+#endif
+
+#include "../../../OrthancFramework/Sources/MallocMemoryBuffer.h"
+#include "../Include/orthanc/OrthancCPlugin.h"
+
+#include <json/value.h>
+
+namespace Orthanc
+{
+  class PluginMemoryBuffer64 : public IMemoryBuffer
+  {
+  private:
+    OrthancPluginMemoryBuffer64  buffer_;
+
+    void SanityCheck() const;
+
+  public:
+    PluginMemoryBuffer64();
+
+    virtual ~PluginMemoryBuffer64()
+    {
+      Clear();
+    }
+
+    virtual void MoveToString(std::string& target) ORTHANC_OVERRIDE;
+
+    virtual const void* GetData() const ORTHANC_OVERRIDE;
+
+    virtual size_t GetSize() const ORTHANC_OVERRIDE;
+
+    OrthancPluginMemoryBuffer64* GetObject()
+    {
+      return &buffer_;
+    }
+
+    void Release(OrthancPluginMemoryBuffer64* target);
+
+    void Clear();
+
+    void Resize(size_t size);
+
+    void Assign(const void* data,
+                size_t size);
+
+    void Assign(const std::string& data);
+  };
+}
--- a/OrthancServer/Plugins/Engine/PluginsJob.cpp	Wed Jun 11 20:51:30 2025 +0200
+++ b/OrthancServer/Plugins/Engine/PluginsJob.cpp	Thu Jun 12 12:29:39 2025 +0200
@@ -32,6 +32,7 @@
 #include "../../../OrthancFramework/Sources/Logging.h"
 #include "../../../OrthancFramework/Sources/OrthancException.h"
 #include "../../../OrthancFramework/Sources/Toolbox.h"
+#include "PluginMemoryBuffer32.h"
 
 #include <cassert>
 
@@ -152,53 +153,11 @@
     return parameters_.getProgress(parameters_.job);
   }
 
-
-  namespace
-  {
-    class MemoryBufferRaii : public boost::noncopyable
-    {
-    private:
-      OrthancPluginMemoryBuffer  buffer_;
-
-    public:
-      MemoryBufferRaii()
-      {
-        buffer_.size = 0;
-        buffer_.data = NULL;
-      }
-
-      ~MemoryBufferRaii()
-      {
-        if (buffer_.size != 0)
-        {
-          free(buffer_.data);
-        }
-      }
-
-      OrthancPluginMemoryBuffer* GetObject()
-      {
-        return &buffer_;
-      }
-
-      void ToJsonObject(Json::Value& target) const
-      {
-        if ((buffer_.data == NULL && buffer_.size != 0) ||
-            (buffer_.data != NULL && buffer_.size == 0) ||
-            !Toolbox::ReadJson(target, buffer_.data, buffer_.size) ||
-            target.type() != Json::objectValue)
-        {
-          throw OrthancException(ErrorCode_Plugin,
-                                 "A job plugin must provide a JSON object as its public content and as its serialization");
-        }
-      }
-    };
-  }
-  
   void PluginsJob::GetPublicContent(Json::Value& value) const
   {
     if (parameters_.getContent != NULL)
     {
-      MemoryBufferRaii target;
+      PluginMemoryBuffer32 target;
 
       OrthancPluginErrorCode code = parameters_.getContent(target.GetObject(), parameters_.job);
 
@@ -236,7 +195,7 @@
   {
     if (parameters_.getSerialized != NULL)
     {
-      MemoryBufferRaii target;
+      PluginMemoryBuffer32 target;
 
       int32_t code = parameters_.getContent(target.GetObject(), parameters_.job);
 
--- a/OrthancServer/Sources/ServerContext.cpp	Wed Jun 11 20:51:30 2025 +0200
+++ b/OrthancServer/Sources/ServerContext.cpp	Thu Jun 12 12:29:39 2025 +0200
@@ -910,7 +910,7 @@
 
     // WARNING: The scope of "modifiedBuffer" and "modifiedDicom" must
     // be the same as that of "dicom"
-    MallocMemoryBuffer modifiedBuffer;
+    PluginMemoryBuffer64 modifiedBuffer;
     std::unique_ptr<DicomInstanceToStore> modifiedDicom;
 
 #if ORTHANC_ENABLE_PLUGINS == 1