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