Orthanc/OrthancFramework/Sources/Compression/ZlibCompressor.cpp
2025-06-23 19:07:37 +05:30

165 lines
4.5 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 "ZlibCompressor.h"
#include "../Endianness.h"
#include "../OrthancException.h"
#include "../Logging.h"
#include <stdio.h>
#include <string.h>
#include <zlib.h>
namespace Orthanc
{
ZlibCompressor::ZlibCompressor()
{
SetPrefixWithUncompressedSize(true);
}
void ZlibCompressor::Compress(std::string& compressed,
const void* uncompressed,
size_t uncompressedSize)
{
if (uncompressedSize == 0)
{
compressed.clear();
return;
}
uLongf compressedSize = compressBound(static_cast<uLong>(uncompressedSize))
+ 1024 /* security margin */;
if (compressedSize == 0)
{
compressedSize = 1;
}
uint8_t* target;
if (HasPrefixWithUncompressedSize())
{
compressed.resize(compressedSize + sizeof(uint64_t));
target = reinterpret_cast<uint8_t*>(&compressed[0]) + sizeof(uint64_t);
}
else
{
compressed.resize(compressedSize);
target = reinterpret_cast<uint8_t*>(&compressed[0]);
}
int error = compress2(target,
&compressedSize,
const_cast<Bytef *>(static_cast<const Bytef *>(uncompressed)),
static_cast<uLong>(uncompressedSize),
GetCompressionLevel());
if (error != Z_OK)
{
compressed.clear();
switch (error)
{
case Z_MEM_ERROR:
throw OrthancException(ErrorCode_NotEnoughMemory);
default:
throw OrthancException(ErrorCode_InternalError);
}
}
// The compression was successful
if (HasPrefixWithUncompressedSize())
{
uint64_t s = static_cast<uint64_t>(uncompressedSize);
// New in Orthanc 1.9.0: Explicitly use litte-endian encoding in size prefix
s = htole64(s);
memcpy(&compressed[0], &s, sizeof(uint64_t));
compressed.resize(compressedSize + sizeof(uint64_t));
}
else
{
compressed.resize(compressedSize);
}
}
void ZlibCompressor::Uncompress(std::string& uncompressed,
const void* compressed,
size_t compressedSize)
{
if (compressedSize == 0)
{
uncompressed.clear();
return;
}
if (!HasPrefixWithUncompressedSize())
{
throw OrthancException(ErrorCode_InternalError,
"Cannot guess the uncompressed size of a zlib-encoded buffer");
}
uint64_t uncompressedSize = ReadUncompressedSizePrefix(compressed, compressedSize);
// New in Orthanc 1.9.0: Explicitly use litte-endian encoding in size prefix
uncompressedSize = le64toh(uncompressedSize);
try
{
uncompressed.resize(static_cast<size_t>(uncompressedSize));
}
catch (...)
{
throw OrthancException(ErrorCode_NotEnoughMemory);
}
uLongf tmp = static_cast<uLongf>(uncompressedSize);
int error = uncompress
(reinterpret_cast<uint8_t*>(&uncompressed[0]),
&tmp,
reinterpret_cast<const uint8_t*>(compressed) + sizeof(uint64_t),
static_cast<uLong>(compressedSize - sizeof(uint64_t)));
if (error != Z_OK)
{
uncompressed.clear();
switch (error)
{
case Z_DATA_ERROR:
throw OrthancException(ErrorCode_CorruptedFile);
case Z_MEM_ERROR:
throw OrthancException(ErrorCode_NotEnoughMemory);
default:
throw OrthancException(ErrorCode_InternalError);
}
}
}
}