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