Mercurial > hg > orthanc
changeset 6635:2c137c796946 limited-memory
integration mainline->limited-memory
| author | Sebastien Jodogne <s.jodogne@gmail.com> |
|---|---|
| date | Fri, 20 Mar 2026 17:43:50 +0100 |
| parents | a0bbb4d460b8 (diff) ab12547ac3df (current diff) |
| children | |
| files | |
| diffstat | 22 files changed, 403 insertions(+), 15 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Fri Mar 20 17:05:49 2026 +0100 +++ b/NEWS Fri Mar 20 17:43:50 2026 +0100 @@ -4,6 +4,8 @@ General ------- +* Experimental WIP: try to limit the memory used by the HTTP server when multiple HTTP clients upload + large DICOM files at the same time. Right now, the limit is hardcoded to 8GB. Check the TODO-MEM in the code. * New experimental configuration "PatientLevelEnabled" (TODO: work in progree) REST API
--- a/OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake Fri Mar 20 17:05:49 2026 +0100 +++ b/OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake Fri Mar 20 17:43:50 2026 +0100 @@ -178,6 +178,7 @@ ${CMAKE_CURRENT_LIST_DIR}/../../Sources/HttpServer/StringMatcher.cpp ${CMAKE_CURRENT_LIST_DIR}/../../Sources/Logging.cpp ${CMAKE_CURRENT_LIST_DIR}/../../Sources/MallocMemoryBuffer.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../Sources/MemoryManagedString.cpp ${CMAKE_CURRENT_LIST_DIR}/../../Sources/OrthancException.cpp ${CMAKE_CURRENT_LIST_DIR}/../../Sources/OrthancFramework.cpp ${CMAKE_CURRENT_LIST_DIR}/../../Sources/RestApi/RestApiHierarchy.cpp
--- a/OrthancFramework/Sources/Compression/ZipReader.cpp Fri Mar 20 17:05:49 2026 +0100 +++ b/OrthancFramework/Sources/Compression/ZipReader.cpp Fri Mar 20 17:43:50 2026 +0100 @@ -375,6 +375,59 @@ } } + // TODO-MEM: find a way to share code between these 2 overloads + bool ZipReader::ReadNextFile(std::string& filename, + MemoryManagedString& content) + { + assert(pimpl_->unzip_ != NULL); + + if (pimpl_->done_) + { + return false; + } + else + { + unz_file_info64_s info; + if (unzGetCurrentFileInfo64(pimpl_->unzip_, &info, NULL, 0, NULL, 0, NULL, 0) != 0) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + + filename.resize(info.size_filename); + if (!filename.empty() && + unzGetCurrentFileInfo64(pimpl_->unzip_, &info, &filename[0], + static_cast<uLong>(filename.size()), NULL, 0, NULL, 0) != 0) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + + content.resize(info.uncompressed_size); + + if (!content.empty()) + { + if (unzOpenCurrentFile(pimpl_->unzip_) == 0) + { + bool success = (unzReadCurrentFile(pimpl_->unzip_, &content[0], + static_cast<uLong>(content.size())) != 0); + + if (unzCloseCurrentFile(pimpl_->unzip_) != 0 || + !success) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + } + else + { + throw OrthancException(ErrorCode_BadFileFormat, "Invalid file or unsupported compression method (e.g. Deflate64)"); + } + } + + pimpl_->done_ = (unzGoToNextFile(pimpl_->unzip_) != 0); + + return true; + } + } + ZipReader* ZipReader::CreateFromMemory(const void* buffer, size_t size)
--- a/OrthancFramework/Sources/Compression/ZipReader.h Fri Mar 20 17:05:49 2026 +0100 +++ b/OrthancFramework/Sources/Compression/ZipReader.h Fri Mar 20 17:43:50 2026 +0100 @@ -43,6 +43,7 @@ #include <string> #include <boost/noncopyable.hpp> #include <boost/shared_ptr.hpp> +#include "../MemoryManagedString.h" #if ORTHANC_SANDBOXED != 1 # include <boost/filesystem.hpp> @@ -69,7 +70,10 @@ bool ReadNextFile(std::string& filename, std::string& content); - + + bool ReadNextFile(std::string& filename, + MemoryManagedString& content); + static ZipReader* CreateFromMemory(const void* buffer, size_t size);
--- a/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp Fri Mar 20 17:05:49 2026 +0100 +++ b/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp Fri Mar 20 17:43:50 2026 +0100 @@ -2603,6 +2603,11 @@ DcmFileFormat* FromDcmtkBridge::LoadFromMemoryBuffer(const void* buffer, size_t size) { + if (!DicomMap::IsDicomFile(buffer, size)) + { + throw OrthancException(ErrorCode_BadFileFormat, "Not a DICOM file"); + } + DcmInputBufferStream is; if (size > 0) {
--- a/OrthancFramework/Sources/FileBuffer.cpp Fri Mar 20 17:05:49 2026 +0100 +++ b/OrthancFramework/Sources/FileBuffer.cpp Fri Mar 20 17:43:50 2026 +0100 @@ -88,6 +88,17 @@ file_.Read(target); } + + void Read(MemoryManagedString& target) + { + if (isWriting_) + { + stream_.close(); + isWriting_ = false; + } + + file_.Read(target); + } }; @@ -110,4 +121,11 @@ assert(pimpl_.get() != NULL); pimpl_->Read(target); } + + + void FileBuffer::Read(MemoryManagedString& target) + { + assert(pimpl_.get() != NULL); + pimpl_->Read(target); + } }
--- a/OrthancFramework/Sources/FileBuffer.h Fri Mar 20 17:05:49 2026 +0100 +++ b/OrthancFramework/Sources/FileBuffer.h Fri Mar 20 17:43:50 2026 +0100 @@ -36,7 +36,7 @@ #include <boost/noncopyable.hpp> #include <boost/shared_ptr.hpp> - +#include "MemoryManagedString.h" namespace Orthanc { @@ -53,5 +53,7 @@ size_t size); void Read(std::string& target); + + void Read(MemoryManagedString& target); }; }
--- a/OrthancFramework/Sources/HttpServer/HttpServer.cpp Fri Mar 20 17:05:49 2026 +0100 +++ b/OrthancFramework/Sources/HttpServer/HttpServer.cpp Fri Mar 20 17:43:50 2026 +0100 @@ -30,6 +30,7 @@ #include "../ChunkedBuffer.h" #include "../FileBuffer.h" #include "../Logging.h" +#include "../MemoryManagedString.h" #include "../OrthancException.h" #include "../TemporaryFile.h" #include "../SystemToolbox.h" @@ -480,7 +481,7 @@ const std::string& username, const UriComponents& uri, const std::map<std::string, std::string>& headers, - const std::string& body, + const MemoryManagedString& body, const std::string& boundary, const std::string& authenticationPayload) { @@ -488,12 +489,12 @@ MultipartStreamReader reader(boundary); reader.SetHandler(handler); - reader.AddChunk(body); + reader.AddChunk(body.c_str(), body.size()); reader.CloseStream(); } - static PostDataStatus ReadBodyWithContentLength(std::string& body, + static PostDataStatus ReadBodyWithContentLength(MemoryManagedString& body, struct mg_connection *connection, const std::string& contentLength) { @@ -533,7 +534,7 @@ } - static PostDataStatus ReadBodyWithoutContentLength(std::string& body, + static PostDataStatus ReadBodyWithoutContentLength(MemoryManagedString& body, struct mg_connection *connection) { // Store the individual chunks in a temporary file, then read it @@ -565,7 +566,7 @@ } - static PostDataStatus ReadBodyToString(std::string& body, + static PostDataStatus ReadBodyToString(MemoryManagedString& body, struct mg_connection *connection, const HttpToolbox::Arguments& headers) { @@ -593,7 +594,7 @@ if (contentLength != headers.end()) { // "Content-Length" is available - std::string body; + MemoryManagedString body; PostDataStatus status = ReadBodyWithContentLength(body, connection, contentLength->second); if (status == PostDataStatus_Success && @@ -1039,10 +1040,11 @@ else if (method == "PUT") { #if CIVETWEB_HAS_WEBDAV_WRITING == 1 - std::string body; + MemoryManagedString body; if (ReadBodyToString(body, connection, headers) == PostDataStatus_Success) { - if (bucket->second->StoreFile(body, path)) + std::string bodyStr(body.begin(), body.end()); // TODO-MEM: avoid this copy and use a MemoryManagedString in StoreFile too + if (bucket->second->StoreFile(bodyStr, path)) { //output.SendStatus(HttpStatus_200_Ok); output.SendStatus(HttpStatus_201_Created); @@ -1425,8 +1427,8 @@ // Extract the body of the request for PUT and POST, or process // the body as a stream + MemoryManagedString body; - std::string body; if (method == HttpMethod_Post || method == HttpMethod_Put) {
--- a/OrthancFramework/Sources/HttpServer/HttpServer.h Fri Mar 20 17:05:49 2026 +0100 +++ b/OrthancFramework/Sources/HttpServer/HttpServer.h Fri Mar 20 17:43:50 2026 +0100 @@ -51,6 +51,7 @@ #include "IIncomingHttpRequestFilter.h" #include "../MetricsRegistry.h" +#include "../MemoryManagedString.h" #include <list> #include <map> @@ -234,7 +235,7 @@ const std::string& username, const UriComponents& uri, const std::map<std::string, std::string>& headers, - const std::string& body, + const MemoryManagedString& body, const std::string& boundary, const std::string& authenticationPayload);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancFramework/Sources/MemoryManagedString.cpp Fri Mar 20 17:43:50 2026 +0100 @@ -0,0 +1,85 @@ +/** + * 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-2026 Orthanc Team SRL, Belgium + * Copyright (C) 2021-2026 Sebastien Jodogne, ICTEAM UCLouvain, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/>. + **/ + + +#include "MemoryManagedString.h" + +#include "OrthancException.h" +#include "MultiThreading/Semaphore.h" +#include <boost/lexical_cast.hpp> +#include "Logging.h" + + +namespace Orthanc +{ + static std::unique_ptr<Semaphore> availableMemorySemaphore_; + static size_t maximumMemorySize_ = 0; + + void LimitedMemoryAllocator::Initialize(size_t maximumMemorySize) + { + if (availableMemorySemaphore_.get() != NULL) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + maximumMemorySize_ = maximumMemorySize; + availableMemorySemaphore_.reset(new Semaphore(maximumMemorySize)); + } + + void* LimitedMemoryAllocator::Allocate(size_t elemSize, size_t numElem) + { + if (availableMemorySemaphore_.get() == NULL) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + size_t requestedSize = numElem * elemSize; + + if (requestedSize > maximumMemorySize_) + { + throw OrthancException(ErrorCode_NotEnoughMemory, "Trying to allocate a buffer ( " + boost::lexical_cast<std::string>(requestedSize) + " bytes) that is larger than the maximum memory size ( " + boost::lexical_cast<std::string>(maximumMemorySize_) + " bytes)"); + } + + // wait until there is enough memory available + availableMemorySemaphore_->Acquire(requestedSize); + + // LOG(TRACE) << "Reserved " << requestedSize << " bytes in memory. Remaining size: " << availableMemorySemaphore_->GetAvailableResourcesCount() << " bytes"; + + void* p = malloc(numElem * elemSize); + if (!p) + { + throw std::bad_alloc(); // the allocation might still fail since we don't track the whole memory + } + return p; + } + + void LimitedMemoryAllocator::Deallocate(void* p, size_t elemSize, size_t numElem) + { + free(p); + availableMemorySemaphore_->Release(elemSize * numElem); + + // LOG(TRACE) << "Released " << (elemSize * numElem) << " bytes in memory. Remaining size: " << availableMemorySemaphore_->GetAvailableResourcesCount() << " bytes"; + } + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancFramework/Sources/MemoryManagedString.h Fri Mar 20 17:43:50 2026 +0100 @@ -0,0 +1,127 @@ +/** + * 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-2026 Orthanc Team SRL, Belgium + * Copyright (C) 2021-2026 Sebastien Jodogne, ICTEAM UCLouvain, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include <memory> +#include <cstdlib> +#include <string> + +namespace Orthanc +{ + template<typename T> class LimitedStdAllocator; + + // TODO-MEM: move this to a dedicated file + class LimitedMemoryAllocator + { + public: + static void Initialize(size_t maximumMemorySize); + protected: + static void* Allocate(size_t elemSize, size_t numElem); + + static void Deallocate(void* p, size_t elemSize, size_t numElem); + + template<typename T>friend class LimitedStdAllocator; + }; + + + template<typename T> + class LimitedStdAllocator + { + public: + typedef T value_type; + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + // Rebind to allow allocator for other types + template<typename U> + struct rebind + { + typedef LimitedStdAllocator<U> other; + }; + + LimitedStdAllocator() + { + } + + template<typename U> + LimitedStdAllocator(const LimitedStdAllocator<U>&) + { + } + + pointer allocate(size_type num, const void* hint = 0) + { + return static_cast<pointer>(LimitedMemoryAllocator::Allocate(sizeof(T), num)); + } + + void deallocate(pointer p, size_type num) + { + LimitedMemoryAllocator::Deallocate(p, sizeof(T), num); + } + + void construct(pointer p, const T& val) + { + new (p) T(val); + } + + void destroy(pointer p) + { + p->~T(); + } + + pointer address(reference x) const + { + return &x; + } + + const_pointer address(const_reference x) const + { + return &x; + } + + size_type max_size() const + { + return size_type(-1) / sizeof(T); + } + }; + + + // // Equality check + // template<typename T1, typename T2> + // bool operator==(const LimitedStdAllocator<T1>&, const LimitedStdAllocator<T2>&) + // { + // return true; + // } + + // template<typename T1, typename T2> + // bool operator!=(const LimitedStdAllocator<T1>&, const LimitedStdAllocator<T2>&) throw() { + // return false; + // } + + typedef std::basic_string<char, std::char_traits<char>, LimitedStdAllocator<char> > MemoryManagedString; +}
--- a/OrthancFramework/Sources/SystemToolbox.cpp Fri Mar 20 17:05:49 2026 +0100 +++ b/OrthancFramework/Sources/SystemToolbox.cpp Fri Mar 20 17:43:50 2026 +0100 @@ -266,6 +266,52 @@ } } + // TODO-MEM: find a way to share code between these 2 overloads + void SystemToolbox::ReadFile(MemoryManagedString& content, + const boost::filesystem::path& path, + bool log) + { + if (!IsRegularFile(path)) + { + throw OrthancException(ErrorCode_RegularFileExpected, + "The path does not point to a regular file: " + PathToUtf8(path), log); + } + + try + { + boost::filesystem::ifstream f; + f.open(path, std::ifstream::in | std::ifstream::binary); + if (!f.good()) + { + throw OrthancException(ErrorCode_InexistentFile, "File not found: " + PathToUtf8(path), log); + } + + std::streamsize size = GetStreamSize(f); + content.resize(static_cast<size_t>(size)); + + if (static_cast<std::streamsize>(content.size()) != size) + { + throw OrthancException(ErrorCode_InternalError, + "Reading a file that is too large for a 32bit architecture"); + } + + if (size != 0) + { + f.read(&content[0], size); + } + + f.close(); + } + catch (boost::filesystem::filesystem_error&) + { + throw OrthancException(ErrorCode_InexistentFile); + } + catch (...) // To catch "std::system_error&" in C++11 + { + throw OrthancException(ErrorCode_InexistentFile); + } + } + bool SystemToolbox::ReadHeader(std::string& header, const boost::filesystem::path& path,
--- a/OrthancFramework/Sources/SystemToolbox.h Fri Mar 20 17:05:49 2026 +0100 +++ b/OrthancFramework/Sources/SystemToolbox.h Fri Mar 20 17:43:50 2026 +0100 @@ -45,6 +45,7 @@ #include <string> #include <stdint.h> #include <boost/filesystem.hpp> +#include "MemoryManagedString.h" // Note: The use of "boost::filesystem::path" is mandatory to handle // non ASCII-only path on Windows @@ -70,6 +71,16 @@ ReadFile(content, path, true /* log */); } + static void ReadFile(MemoryManagedString& content, + const boost::filesystem::path& path, + bool log); + + static inline void ReadFile(MemoryManagedString& content, + const boost::filesystem::path& path) + { + ReadFile(content, path, true /* log */); + } + static bool ReadHeader(std::string& header, const boost::filesystem::path& path, size_t headerSize);
--- a/OrthancFramework/Sources/TemporaryFile.cpp Fri Mar 20 17:05:49 2026 +0100 +++ b/OrthancFramework/Sources/TemporaryFile.cpp Fri Mar 20 17:43:50 2026 +0100 @@ -129,6 +129,21 @@ } + void TemporaryFile::Read(MemoryManagedString& content) const + { + try + { + SystemToolbox::ReadFile(content, path_); + } + catch (OrthancException& e) + { + throw OrthancException(e.GetErrorCode(), + "Can't read temporary file \"" + SystemToolbox::PathToUtf8(path_) + + "\": Another process has corrupted the temporary directory"); + } + } + + void TemporaryFile::Touch() { std::string empty;
--- a/OrthancFramework/Sources/TemporaryFile.h Fri Mar 20 17:05:49 2026 +0100 +++ b/OrthancFramework/Sources/TemporaryFile.h Fri Mar 20 17:43:50 2026 +0100 @@ -38,6 +38,7 @@ #include <boost/filesystem.hpp> #include <stdint.h> #include <string> +#include "MemoryManagedString.h" namespace Orthanc { @@ -60,6 +61,8 @@ void Read(std::string& content) const; + void Read(MemoryManagedString& content) const; + void Touch(); uint64_t GetFileSize() const;
--- a/OrthancServer/Plugins/Samples/ConnectivityChecks/OrthancFrameworkDependencies.cpp Fri Mar 20 17:05:49 2026 +0100 +++ b/OrthancServer/Plugins/Samples/ConnectivityChecks/OrthancFrameworkDependencies.cpp Fri Mar 20 17:43:50 2026 +0100 @@ -50,5 +50,7 @@ #include "../../../../OrthancFramework/Sources/Enumerations.cpp" #include "../../../../OrthancFramework/Sources/Logging.cpp" #include "../../../../OrthancFramework/Sources/OrthancException.cpp" +#include "../../../../OrthancFramework/Sources/MemoryManagedString.cpp" +#include "../../../../OrthancFramework/Sources/MultiThreading/Semaphore.cpp" #include "../../../../OrthancFramework/Sources/SystemToolbox.cpp" #include "../../../../OrthancFramework/Sources/Toolbox.cpp"
--- a/OrthancServer/Plugins/Samples/DelayedDeletion/OrthancFrameworkDependencies.cpp Fri Mar 20 17:05:49 2026 +0100 +++ b/OrthancServer/Plugins/Samples/DelayedDeletion/OrthancFrameworkDependencies.cpp Fri Mar 20 17:43:50 2026 +0100 @@ -53,7 +53,9 @@ #include "../../../../OrthancFramework/Sources/Enumerations.cpp" #include "../../../../OrthancFramework/Sources/FileStorage/FilesystemStorage.cpp" #include "../../../../OrthancFramework/Sources/Logging.cpp" +#include "../../../../OrthancFramework/Sources/MemoryManagedString.cpp" #include "../../../../OrthancFramework/Sources/MultiThreading/SharedMessageQueue.cpp" +#include "../../../../OrthancFramework/Sources/MultiThreading/Semaphore.cpp" #include "../../../../OrthancFramework/Sources/OrthancException.cpp" #include "../../../../OrthancFramework/Sources/SQLite/Connection.cpp" #include "../../../../OrthancFramework/Sources/SQLite/FunctionContext.cpp"
--- a/OrthancServer/Plugins/Samples/MultitenantDicom/OrthancFrameworkDependencies.cpp Fri Mar 20 17:05:49 2026 +0100 +++ b/OrthancServer/Plugins/Samples/MultitenantDicom/OrthancFrameworkDependencies.cpp Fri Mar 20 17:43:50 2026 +0100 @@ -78,9 +78,11 @@ #include "../../../../OrthancFramework/Sources/Images/PngReader.cpp" #include "../../../../OrthancFramework/Sources/Images/PngWriter.cpp" #include "../../../../OrthancFramework/Sources/Logging.cpp" +#include "../../../../OrthancFramework/Sources/MemoryManagedString.cpp" #include "../../../../OrthancFramework/Sources/MetricsRegistry.cpp" #include "../../../../OrthancFramework/Sources/MultiThreading/RunnableWorkersPool.cpp" #include "../../../../OrthancFramework/Sources/MultiThreading/SharedMessageQueue.cpp" +#include "../../../../OrthancFramework/Sources/MultiThreading/Semaphore.cpp" #include "../../../../OrthancFramework/Sources/OrthancException.cpp" #include "../../../../OrthancFramework/Sources/OrthancFramework.cpp" #include "../../../../OrthancFramework/Sources/RestApi/RestApiOutput.cpp"
--- a/OrthancServer/Plugins/Samples/ServeFolders/OrthancFrameworkDependencies.cpp Fri Mar 20 17:05:49 2026 +0100 +++ b/OrthancServer/Plugins/Samples/ServeFolders/OrthancFrameworkDependencies.cpp Fri Mar 20 17:43:50 2026 +0100 @@ -50,5 +50,7 @@ #include "../../../../OrthancFramework/Sources/Enumerations.cpp" #include "../../../../OrthancFramework/Sources/Logging.cpp" #include "../../../../OrthancFramework/Sources/OrthancException.cpp" +#include "../../../../OrthancFramework/Sources/MemoryManagedString.cpp" +#include "../../../../OrthancFramework/Sources/MultiThreading/Semaphore.cpp" #include "../../../../OrthancFramework/Sources/SystemToolbox.cpp" #include "../../../../OrthancFramework/Sources/Toolbox.cpp"
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp Fri Mar 20 17:05:49 2026 +0100 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestApi.cpp Fri Mar 20 17:43:50 2026 +0100 @@ -177,14 +177,15 @@ Json::Value answer = Json::arrayValue; - std::string filename, content; + std::string filename; + MemoryManagedString content; while (reader->ReadNextFile(filename, content)) { if (!content.empty()) { LOG(INFO) << "Uploading DICOM file from ZIP archive: " << filename; - std::unique_ptr<DicomInstanceToStore> toStore(DicomInstanceToStore::CreateFromBuffer(content)); + std::unique_ptr<DicomInstanceToStore> toStore(DicomInstanceToStore::CreateFromBuffer(content.c_str(), content.size())); toStore->SetOrigin(DicomInstanceOrigin::FromRest(call)); try
--- a/OrthancServer/Sources/OrthancWebDav.cpp Fri Mar 20 17:05:49 2026 +0100 +++ b/OrthancServer/Sources/OrthancWebDav.cpp Fri Mar 20 17:43:50 2026 +0100 @@ -1213,7 +1213,7 @@ std::unique_ptr<ZipReader> reader(ZipReader::CreateFromMemory(content)); std::string filename, uncompressedFile; - while (reader->ReadNextFile(filename, uncompressedFile)) + while (reader->ReadNextFile(filename, uncompressedFile)) // TODO-MEM: use MemoryManagedString { if (!uncompressedFile.empty()) {
--- a/OrthancServer/Sources/main.cpp Fri Mar 20 17:05:49 2026 +0100 +++ b/OrthancServer/Sources/main.cpp Fri Mar 20 17:43:50 2026 +0100 @@ -1959,6 +1959,10 @@ Logging::SetCurrentThreadName("MAIN"); SetGlobalVerbosity(Verbosity_Default); + uint64_t maxMemorySize = 8ul * 1024ul * 1024ul * 1024ul; // TODO-MEM: get this value from the configuration file + maxMemorySize = std::min(maxMemorySize, std::numeric_limits<size_t>::max()); // on 32 bits system, limit the value to 4GB + LimitedMemoryAllocator::Initialize(static_cast<size_t>(maxMemorySize)); + bool upgradeDatabase = false; bool loadJobsFromDatabase = true; boost::filesystem::path configurationFile;
