Mercurial > hg > orthanc
changeset 6163:255fcf2f8541
fix the re-encoding of DICOM files larger than 4GB
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 11 Jun 2025 14:35:44 +0200 (8 days ago) |
parents | 73e9d9248cf3 |
children | 52f87859fec2 2ab75e4e8c91 |
files | NEWS OrthancFramework/Sources/ChunkedBuffer.cpp OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp |
diffstat | 3 files changed, 136 insertions(+), 4 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Mon Jun 02 12:28:45 2025 +0200 +++ b/NEWS Wed Jun 11 14:35:44 2025 +0200 @@ -16,10 +16,10 @@ Patch provided by Yurii (George) from ivtech.dev. With this patch, we observed a 100 fold performance improvement when the "Pending" table contains 1-2 millions files. - * Configuration options "RejectSopClasses" and "RejectedSopClasses" are taken as synonyms. In Orthanc 1.12.6 and 1.12.7, "RejectSopClasses" was used instead of the expected "RejectedSopClasses" spelling. +* Fix the re-encoding of DICOM files larger than 4GB REST API
--- a/OrthancFramework/Sources/ChunkedBuffer.cpp Mon Jun 02 12:28:45 2025 +0200 +++ b/OrthancFramework/Sources/ChunkedBuffer.cpp Wed Jun 11 14:35:44 2025 +0200 @@ -25,6 +25,8 @@ #include "PrecompiledHeaders.h" #include "ChunkedBuffer.h" +#include "OrthancException.h" + #include <cassert> #include <string.h> @@ -54,7 +56,16 @@ else { assert(chunkData != NULL); - chunks_.push_back(new std::string(reinterpret_cast<const char*>(chunkData), chunkSize)); + + try + { + chunks_.push_back(new std::string(reinterpret_cast<const char*>(chunkData), chunkSize)); + } + catch (...) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } + numBytes_ += chunkSize; } } @@ -172,7 +183,15 @@ void ChunkedBuffer::Flatten(std::string& result) { FlushPendingBuffer(); - result.resize(numBytes_); + + try + { + result.resize(numBytes_); + } + catch (...) + { + throw OrthancException(ErrorCode_NotEnoughMemory); + } size_t pos = 0; for (Chunks::iterator it = chunks_.begin();
--- a/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp Mon Jun 02 12:28:45 2025 +0200 +++ b/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp Wed Jun 11 14:35:44 2025 +0200 @@ -38,6 +38,7 @@ #include "FromDcmtkBridge.h" #include "ToDcmtkBridge.h" +#include "../ChunkedBuffer.h" #include "../Compatibility.h" #include "../Logging.h" #include "../Toolbox.h" @@ -167,6 +168,73 @@ namespace { + class ChunkedBufferStream : public DcmOutputStream + { + private: + class Consumer : public DcmConsumer + { + private: + ChunkedBuffer buffer_; + + public: + void Flatten(std::string& buffer) + { + buffer_.Flatten(buffer); + } + + OFBool good() const ORTHANC_OVERRIDE + { + return true; + } + + OFCondition status() const ORTHANC_OVERRIDE + { + return EC_Normal; + } + + OFBool isFlushed() const ORTHANC_OVERRIDE + { + return true; + } + + offile_off_t avail() const ORTHANC_OVERRIDE + { + // since we cannot report "unlimited", let's claim that we can still write 10MB. + // Note that offile_off_t is a signed type. + return 10 * 1024 * 1024; + } + + offile_off_t write(const void *buf, + offile_off_t buflen) ORTHANC_OVERRIDE + { + buffer_.AddChunk(buf, buflen); + return buflen; + } + + void flush() ORTHANC_OVERRIDE + { + // Nothing to flush + } + }; + + Consumer consumer_; + + public: + ChunkedBufferStream() : + DcmOutputStream(&consumer_) + { + } + + void Flatten(std::string& buffer) + { + consumer_.Flatten(buffer); + } + }; + } + + + namespace + { class DictionaryLocker : public boost::noncopyable { private: @@ -1572,7 +1640,12 @@ } - +#if 0 + /** + * This was the implementation in Orthanc <= 1.12.7. This version + * uses "DcmFileFormat::calcElementLength()", which cannot handle + * DICOM files whose size cannot be represented on 32 bits. + **/ static bool SaveToMemoryBufferInternal(std::string& buffer, DcmFileFormat& dicom, E_TransferSyntax xfer, @@ -1619,6 +1692,46 @@ return false; } } +#endif + + +#if 1 + /** + * This is the cleaner implementation used in Orthanc >= 1.12.8, + * which allows to write DICOM files larger than 4GB. + **/ + static bool SaveToMemoryBufferInternal(std::string& buffer, + DcmFileFormat& dicom, + E_TransferSyntax xfer, + std::string& errorMessage) + { + ChunkedBufferStream ob; + + // Fill the (chunked) memory buffer with the meta-header and the dataset + dicom.transferInit(); + OFCondition c = dicom.write(ob, xfer, /*opt_sequenceType*/ EET_ExplicitLength, NULL, + /*opt_groupLength*/ EGL_recalcGL, + /*opt_paddingType*/ EPD_noChange, + /*padlen*/ 0, /*subPadlen*/ 0, /*instanceLength*/ 0, + EWM_updateMeta /* creates new SOP instance UID on lossy */); + dicom.transferEnd(); + + if (c.good()) + { + ob.flush(); + ob.Flatten(buffer); + return true; + } + else + { + // Error + buffer.clear(); + errorMessage = std::string(c.text()); + return false; + } + } +#endif + bool FromDcmtkBridge::SaveToMemoryBuffer(std::string& buffer, DcmDataset& dataSet)