243 lines
5.1 KiB
C++
243 lines
5.1 KiB
C++
/**
|
|
* 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 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 "PrecompiledHeaders.h"
|
|
#include "ChunkedBuffer.h"
|
|
|
|
#include "OrthancException.h"
|
|
|
|
#include <cassert>
|
|
#include <string.h>
|
|
|
|
|
|
namespace Orthanc
|
|
{
|
|
void ChunkedBuffer::Clear()
|
|
{
|
|
numBytes_ = 0;
|
|
pendingPos_ = 0;
|
|
|
|
for (Chunks::iterator it = chunks_.begin();
|
|
it != chunks_.end(); ++it)
|
|
{
|
|
delete *it;
|
|
}
|
|
}
|
|
|
|
|
|
void ChunkedBuffer::AddChunkInternal(const void* chunkData,
|
|
size_t chunkSize)
|
|
{
|
|
if (chunkSize == 0)
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
assert(chunkData != NULL);
|
|
|
|
try
|
|
{
|
|
chunks_.push_back(new std::string(reinterpret_cast<const char*>(chunkData), chunkSize));
|
|
}
|
|
catch (...)
|
|
{
|
|
throw OrthancException(ErrorCode_NotEnoughMemory);
|
|
}
|
|
|
|
numBytes_ += chunkSize;
|
|
}
|
|
}
|
|
|
|
|
|
void ChunkedBuffer::FlushPendingBuffer()
|
|
{
|
|
assert(pendingPos_ <= pendingBuffer_.size());
|
|
|
|
if (!pendingBuffer_.empty())
|
|
{
|
|
AddChunkInternal(pendingBuffer_.c_str(), pendingPos_);
|
|
}
|
|
else
|
|
{
|
|
assert(pendingPos_ == 0);
|
|
}
|
|
|
|
pendingPos_ = 0;
|
|
}
|
|
|
|
|
|
ChunkedBuffer::ChunkedBuffer() :
|
|
numBytes_(0),
|
|
pendingPos_(0)
|
|
{
|
|
pendingBuffer_.resize(16 * 1024); // Default size of the pending buffer: 16KB
|
|
}
|
|
|
|
|
|
ChunkedBuffer::~ChunkedBuffer()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
|
|
size_t ChunkedBuffer::GetNumBytes() const
|
|
{
|
|
return numBytes_ + pendingPos_;
|
|
}
|
|
|
|
|
|
void ChunkedBuffer::SetPendingBufferSize(size_t size)
|
|
{
|
|
FlushPendingBuffer();
|
|
pendingBuffer_.resize(size);
|
|
}
|
|
|
|
|
|
size_t ChunkedBuffer::GetPendingBufferSize() const
|
|
{
|
|
return pendingBuffer_.size();
|
|
}
|
|
|
|
|
|
void ChunkedBuffer::AddChunk(const void* chunkData,
|
|
size_t chunkSize)
|
|
{
|
|
if (chunkSize > 0)
|
|
{
|
|
#if 1
|
|
assert(sizeof(char) == 1);
|
|
|
|
// Optimization if Orthanc >= 1.7.3, to speed up in the presence of many small chunks
|
|
if (pendingPos_ + chunkSize <= pendingBuffer_.size())
|
|
{
|
|
// There remains enough place in the pending buffer
|
|
memcpy(&pendingBuffer_[pendingPos_], chunkData, chunkSize);
|
|
pendingPos_ += chunkSize;
|
|
}
|
|
else
|
|
{
|
|
FlushPendingBuffer();
|
|
|
|
if (!pendingBuffer_.empty() &&
|
|
chunkSize < pendingBuffer_.size())
|
|
{
|
|
memcpy(&pendingBuffer_[0], chunkData, chunkSize);
|
|
pendingPos_ = chunkSize;
|
|
}
|
|
else
|
|
{
|
|
AddChunkInternal(chunkData, chunkSize);
|
|
}
|
|
}
|
|
#else
|
|
// Non-optimized implementation in Orthanc <= 1.7.2
|
|
AddChunkInternal(chunkData, chunkSize);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
void ChunkedBuffer::AddChunk(const std::string& chunk)
|
|
{
|
|
if (chunk.size() > 0)
|
|
{
|
|
AddChunk(&chunk[0], chunk.size());
|
|
}
|
|
}
|
|
|
|
|
|
void ChunkedBuffer::AddChunk(const std::string::const_iterator& begin,
|
|
const std::string::const_iterator& end)
|
|
{
|
|
const size_t s = end - begin;
|
|
|
|
if (s > 0)
|
|
{
|
|
AddChunk(&begin[0], s);
|
|
}
|
|
}
|
|
|
|
|
|
void ChunkedBuffer::Flatten(std::string& result)
|
|
{
|
|
FlushPendingBuffer();
|
|
|
|
if (chunks_.empty())
|
|
{
|
|
if (numBytes_ != 0)
|
|
{
|
|
throw OrthancException(ErrorCode_InternalError);
|
|
}
|
|
|
|
result.clear();
|
|
}
|
|
else if (chunks_.size() == 1)
|
|
{
|
|
// Avoid reallocating a buffer if there is a single chunk
|
|
assert(chunks_.front() != NULL);
|
|
if (chunks_.front()->size() != numBytes_)
|
|
{
|
|
throw OrthancException(ErrorCode_InternalError);
|
|
}
|
|
else
|
|
{
|
|
chunks_.front()->swap(result);
|
|
delete chunks_.front();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
result.resize(numBytes_);
|
|
}
|
|
catch (...)
|
|
{
|
|
throw OrthancException(ErrorCode_NotEnoughMemory);
|
|
}
|
|
|
|
size_t pos = 0;
|
|
for (Chunks::iterator it = chunks_.begin();
|
|
it != chunks_.end(); ++it)
|
|
{
|
|
assert(*it != NULL);
|
|
|
|
size_t s = (*it)->size();
|
|
if (s != 0)
|
|
{
|
|
memcpy(&result[pos], (*it)->c_str(), s);
|
|
pos += s;
|
|
}
|
|
|
|
delete *it;
|
|
}
|
|
}
|
|
|
|
// Reset the data structure
|
|
chunks_.clear();
|
|
numBytes_ = 0;
|
|
}
|
|
}
|