1 // -*- coding: utf-8 -*-
2 //
3 // EmbeddedResource.cxx --- Class for pointing to/accessing an embedded resource
4 // Copyright (C) 2017 Florent Rougon
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Library General Public
8 // License as published by the Free Software Foundation; either
9 // version 2 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Library General Public License for more details.
15 //
16 // You should have received a copy of the GNU Library General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 // MA 02110-1301 USA.
20
21 #include <simgear_config.h>
22
23 #include <string>
24 #include <iosfwd>
25 #include <ios> // std::streamsize
26 #include <ostream>
27 #include <memory> // std::unique_ptr
28 #include <utility> // std::move()
29 #include <algorithm> // std::min()
30 #include <limits> // std::numeric_limits
31 #include <cstddef> // std::size_t, std::ptrdiff_t
32
33 #include <simgear/structure/exception.hxx>
34 #include <simgear/io/iostreams/CharArrayStream.hxx>
35 #include <simgear/io/iostreams/zlibstream.hxx>
36 #include "EmbeddedResource.hxx"
37
38 using std::string;
39 using std::unique_ptr;
40
41 // Inspired by <http://stackoverflow.com/a/21174979/4756009>
42 template<typename Derived, typename Base>
static_unique_ptr_cast(unique_ptr<Base> p)43 static unique_ptr<Derived> static_unique_ptr_cast(unique_ptr<Base> p)
44 {
45 auto d = static_cast<Derived *>(p.release());
46 return unique_ptr<Derived>(d);
47 }
48
49
50 namespace simgear
51 {
52
53 // ***************************************************************************
54 // * AbstractEmbeddedResource class *
55 // ***************************************************************************
56
AbstractEmbeddedResource(const char * data,std::size_t size)57 AbstractEmbeddedResource::AbstractEmbeddedResource(const char *data,
58 std::size_t size)
59 : _data(data),
60 _size(size)
61 { }
62
rawPtr() const63 const char *AbstractEmbeddedResource::rawPtr() const
64 {
65 return _data;
66 }
67
rawSize() const68 std::size_t AbstractEmbeddedResource::rawSize() const
69 {
70 return _size;
71 }
72
str() const73 string AbstractEmbeddedResource::str() const
74 {
75 if (_size > std::numeric_limits<string::size_type>::max()) {
76 throw sg_range_exception(
77 "Resource too large to fit in an std::string (size: " +
78 std::to_string(_size) + " bytes)");
79 }
80
81 return string(_data, _size);
82 }
83
84 // ***************************************************************************
85 // * RawEmbeddedResource class *
86 // ***************************************************************************
87
RawEmbeddedResource(const char * data,std::size_t size)88 RawEmbeddedResource::RawEmbeddedResource(const char *data, std::size_t size)
89 : AbstractEmbeddedResource(data, size)
90 { }
91
92 AbstractEmbeddedResource::CompressionType
compressionType() const93 RawEmbeddedResource::compressionType() const
94 {
95 return AbstractEmbeddedResource::CompressionType::NONE;
96 }
97
compressionDescr() const98 string RawEmbeddedResource::compressionDescr() const
99 {
100 return string("none");
101 }
102
streambuf() const103 unique_ptr<std::streambuf> RawEmbeddedResource::streambuf() const
104 {
105 // This is a read-only variant of CharArrayStreambuf
106 return unique_ptr<std::streambuf>(
107 new ROCharArrayStreambuf(rawPtr(), rawSize()));
108 }
109
istream() const110 unique_ptr<std::istream> RawEmbeddedResource::istream() const
111 {
112 return unique_ptr<std::istream>(new CharArrayIStream(rawPtr(), rawSize()));
113 }
114
115 // ***************************************************************************
116 // * ZlibEmbeddedResource class *
117 // ***************************************************************************
118
ZlibEmbeddedResource(const char * data,std::size_t compressedSize,std::size_t uncompressedSize)119 ZlibEmbeddedResource::ZlibEmbeddedResource(const char *data,
120 std::size_t compressedSize,
121 std::size_t uncompressedSize)
122 : AbstractEmbeddedResource(data, compressedSize),
123 _uncompressedSize(uncompressedSize),
124 _inBuf(nullptr),
125 _inBufSize(262144), // adjusted below in the constructor body
126 _outBuf(nullptr),
127 _outBufSize(262144),
128 _putbackSize(0) // default for best performance
129 {
130 static_assert(262144 <= std::numeric_limits<std::size_t>::max(),
131 "The std::size_t type is unexpectedly small.");
132 // No need to use an input buffer (where compressed data chunks are put for
133 // zlib to read and decompress) larger than the whole compressed resource!
134 _inBufSize = std::min(rawSize(), _inBufSize);
135 }
136
137 AbstractEmbeddedResource::CompressionType
compressionType() const138 ZlibEmbeddedResource::compressionType() const
139 { return AbstractEmbeddedResource::CompressionType::ZLIB; }
140
compressionDescr() const141 string ZlibEmbeddedResource::compressionDescr() const
142 { return string("zlib"); }
143
uncompressedSize() const144 std::size_t ZlibEmbeddedResource::uncompressedSize() const
145 { return _uncompressedSize; }
146
getInputBufferStart()147 char* ZlibEmbeddedResource::getInputBufferStart()
148 { return _inBuf; }
149
setInputBufferStart(char * inBuf)150 void ZlibEmbeddedResource::setInputBufferStart(char* inBuf)
151 { _inBuf = inBuf; }
152
getInputBufferSize()153 std::size_t ZlibEmbeddedResource::getInputBufferSize()
154 { return _inBufSize; }
155
setInputBufferSize(std::size_t size)156 void ZlibEmbeddedResource::setInputBufferSize(std::size_t size)
157 { _inBufSize = size; }
158
getOutputBufferStart()159 char* ZlibEmbeddedResource::getOutputBufferStart()
160 { return _outBuf; }
161
setOutputBufferStart(char * outBuf)162 void ZlibEmbeddedResource::setOutputBufferStart(char* outBuf)
163 { _outBuf = outBuf; }
164
getOutputBufferSize()165 std::size_t ZlibEmbeddedResource::getOutputBufferSize()
166 { return _outBufSize; }
167
setOutputBufferSize(std::size_t size)168 void ZlibEmbeddedResource::setOutputBufferSize(std::size_t size)
169 { _outBufSize = size; }
170
getPutbackSize()171 std::size_t ZlibEmbeddedResource::getPutbackSize()
172 { return _putbackSize; }
173
setPutbackSize(std::size_t size)174 void ZlibEmbeddedResource::setPutbackSize(std::size_t size)
175 { _putbackSize = size; }
176
streambuf() const177 unique_ptr<std::streambuf> ZlibEmbeddedResource::streambuf() const
178 {
179 unique_ptr<CharArrayIStream> rawReaderIStream(
180 new CharArrayIStream(rawPtr(), rawSize()));
181
182 return unique_ptr<std::streambuf>(
183 new ZlibDecompressorIStreambuf(
184 std::move(rawReaderIStream),
185 SGPath(), // rawReaderIStream isn't bound to a file
186 ZLibCompressionFormat::ZLIB,
187 _inBuf, _inBufSize, _outBuf, _outBufSize, _putbackSize));
188 }
189
istream() const190 unique_ptr<std::istream> ZlibEmbeddedResource::istream() const
191 {
192 unique_ptr<CharArrayIStream> rawReaderIStream(
193 new CharArrayIStream(rawPtr(), rawSize()));
194
195 return unique_ptr<std::istream>(
196 new ZlibDecompressorIStream(
197 std::move(rawReaderIStream),
198 SGPath(), // rawReaderIStream isn't bound to a file
199 ZLibCompressionFormat::ZLIB,
200 _inBuf, _inBufSize, _outBuf, _outBufSize, _putbackSize));
201 }
202
str() const203 std::string ZlibEmbeddedResource::str() const
204 {
205 static constexpr std::size_t bufSize = 65536;
206 static_assert(bufSize <= std::numeric_limits<std::streamsize>::max(),
207 "Type std::streamsize is unexpectedly small");
208 static_assert(bufSize <= std::numeric_limits<string::size_type>::max(),
209 "Type std::string::size_type is unexpectedly small");
210 unique_ptr<char[]> buf(new char[bufSize]);
211
212 auto decompressor =
213 static_unique_ptr_cast<ZlibDecompressorIStream>(istream());
214 std::streamsize nbCharsRead;
215 string result;
216
217 if (_uncompressedSize > std::numeric_limits<string::size_type>::max()) {
218 throw sg_range_exception(
219 "Resource too large to fit in an std::string (uncompressed size: "
220 + std::to_string(_uncompressedSize) + " bytes)");
221 } else {
222 result.reserve(static_cast<string::size_type>(_uncompressedSize));
223 }
224
225 do {
226 decompressor->read(buf.get(), bufSize);
227 nbCharsRead = decompressor->gcount();
228
229 if (nbCharsRead > 0) {
230 result.append(buf.get(), nbCharsRead);
231 }
232 } while (*decompressor);
233
234 // decompressor->fail() would *not* indicate an error, due to the semantics
235 // of std::istream::read().
236 if (decompressor->bad()) {
237 throw sg_io_exception("Error while extracting a compressed resource");
238 }
239
240 return result;
241 }
242
243 // ***************************************************************************
244 // * Stream insertion operators *
245 // ***************************************************************************
operator <<(std::ostream & os,const RawEmbeddedResource & resource)246 std::ostream& operator<<(std::ostream& os,
247 const RawEmbeddedResource& resource)
248 { // This won't escape double quotes, backslashes, etc. in resource.str().
249 return os << "RawEmbeddedResource:\n"
250 " compressionType = \"" << resource.compressionDescr() << "\"\n"
251 " rawPtr = " << (void*) resource.rawPtr() << "\n"
252 " rawSize = " << resource.rawSize();
253 }
254
operator <<(std::ostream & os,const ZlibEmbeddedResource & resource)255 std::ostream& operator<<(std::ostream& os,
256 const ZlibEmbeddedResource& resource)
257 { // This won't escape double quotes, backslashes, etc. in resource.str().
258 return os << "ZlibEmbeddedResource:\n"
259 " compressionType = \"" << resource.compressionDescr() << "\"\n"
260 " rawPtr = " << (void*) resource.rawPtr() << "\n"
261 " rawSize = " << resource.rawSize() << "\n"
262 " uncompressedSize = " << resource.uncompressedSize();
263 }
264
265 } // of namespace simgear
266