1 /** 2 * Orthanc - A Lightweight, RESTful DICOM Store 3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics 4 * Department, University Hospital of Liege, Belgium 5 * Copyright (C) 2017-2021 Osimis S.A., Belgium 6 * 7 * This program is free software: you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public License 9 * as published by the Free Software Foundation, either version 3 of 10 * the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this program. If not, see 19 * <http://www.gnu.org/licenses/>. 20 **/ 21 22 23 #include "../PrecompiledHeaders.h" 24 #include "ZlibCompressor.h" 25 26 #include "../Endianness.h" 27 #include "../OrthancException.h" 28 #include "../Logging.h" 29 30 #include <stdio.h> 31 #include <string.h> 32 #include <zlib.h> 33 34 namespace Orthanc 35 { ZlibCompressor()36 ZlibCompressor::ZlibCompressor() 37 { 38 SetPrefixWithUncompressedSize(true); 39 } 40 Compress(std::string & compressed,const void * uncompressed,size_t uncompressedSize)41 void ZlibCompressor::Compress(std::string& compressed, 42 const void* uncompressed, 43 size_t uncompressedSize) 44 { 45 if (uncompressedSize == 0) 46 { 47 compressed.clear(); 48 return; 49 } 50 51 uLongf compressedSize = compressBound(static_cast<uLong>(uncompressedSize)) 52 + 1024 /* security margin */; 53 if (compressedSize == 0) 54 { 55 compressedSize = 1; 56 } 57 58 uint8_t* target; 59 if (HasPrefixWithUncompressedSize()) 60 { 61 compressed.resize(compressedSize + sizeof(uint64_t)); 62 target = reinterpret_cast<uint8_t*>(&compressed[0]) + sizeof(uint64_t); 63 } 64 else 65 { 66 compressed.resize(compressedSize); 67 target = reinterpret_cast<uint8_t*>(&compressed[0]); 68 } 69 70 int error = compress2(target, 71 &compressedSize, 72 const_cast<Bytef *>(static_cast<const Bytef *>(uncompressed)), 73 static_cast<uLong>(uncompressedSize), 74 GetCompressionLevel()); 75 76 if (error != Z_OK) 77 { 78 compressed.clear(); 79 80 switch (error) 81 { 82 case Z_MEM_ERROR: 83 throw OrthancException(ErrorCode_NotEnoughMemory); 84 85 default: 86 throw OrthancException(ErrorCode_InternalError); 87 } 88 } 89 90 // The compression was successful 91 if (HasPrefixWithUncompressedSize()) 92 { 93 uint64_t s = static_cast<uint64_t>(uncompressedSize); 94 95 // New in Orthanc 1.9.0: Explicitly use litte-endian encoding in size prefix 96 s = htole64(s); 97 98 memcpy(&compressed[0], &s, sizeof(uint64_t)); 99 compressed.resize(compressedSize + sizeof(uint64_t)); 100 } 101 else 102 { 103 compressed.resize(compressedSize); 104 } 105 } 106 107 Uncompress(std::string & uncompressed,const void * compressed,size_t compressedSize)108 void ZlibCompressor::Uncompress(std::string& uncompressed, 109 const void* compressed, 110 size_t compressedSize) 111 { 112 if (compressedSize == 0) 113 { 114 uncompressed.clear(); 115 return; 116 } 117 118 if (!HasPrefixWithUncompressedSize()) 119 { 120 throw OrthancException(ErrorCode_InternalError, 121 "Cannot guess the uncompressed size of a zlib-encoded buffer"); 122 } 123 124 uint64_t uncompressedSize = ReadUncompressedSizePrefix(compressed, compressedSize); 125 126 // New in Orthanc 1.9.0: Explicitly use litte-endian encoding in size prefix 127 uncompressedSize = le64toh(uncompressedSize); 128 129 try 130 { 131 uncompressed.resize(static_cast<size_t>(uncompressedSize)); 132 } 133 catch (...) 134 { 135 throw OrthancException(ErrorCode_NotEnoughMemory); 136 } 137 138 uLongf tmp = static_cast<uLongf>(uncompressedSize); 139 int error = uncompress 140 (reinterpret_cast<uint8_t*>(&uncompressed[0]), 141 &tmp, 142 reinterpret_cast<const uint8_t*>(compressed) + sizeof(uint64_t), 143 static_cast<uLong>(compressedSize - sizeof(uint64_t))); 144 145 if (error != Z_OK) 146 { 147 uncompressed.clear(); 148 149 switch (error) 150 { 151 case Z_DATA_ERROR: 152 throw OrthancException(ErrorCode_CorruptedFile); 153 154 case Z_MEM_ERROR: 155 throw OrthancException(ErrorCode_NotEnoughMemory); 156 157 default: 158 throw OrthancException(ErrorCode_InternalError); 159 } 160 } 161 } 162 } 163