Anklang 0.3.0-460-gc4ef46ba
ASE — Anklang Sound Engine (C++)

« « « Anklang Documentation
Loading...
Searching...
No Matches
blob.cc
Go to the documentation of this file.
1 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
2#include "blob.hh"
3#include "utils.hh"
4#include "strings.hh"
5#include "internal.hh"
6#include <fcntl.h>
7#include <unistd.h>
8#include <sys/stat.h>
9#include <sys/mman.h>
10#include <zlib.h>
11
12#define BDEBUG(...) Ase::debug ("blob", __VA_ARGS__)
13
14namespace Ase {
15
16static Blob
17error_result (String url, int fallback_errno = EINVAL, String msg = "failed to load")
18{
19 const int saved_errno = errno ? errno : fallback_errno;
20 BDEBUG ("%s %s: %s", msg.c_str(), CQUOTE (url), strerror (saved_errno));
21 errno = saved_errno;
22 return Blob();
23}
24
25// == BlobImpl ==
26class BlobImpl {
27public:
28 String name_;
29 size_t size_;
30 const char *data_;
31 virtual ~BlobImpl () {}
32 virtual String string () = 0;
33 explicit BlobImpl (const String &name, size_t dsize, const char *data) :
34 name_ (name), size_ (dsize), data_ (data)
35 {}
36};
37
38// == NoDelete ==
39struct NoDelete { // Dummy deleter
40 void operator() (const char*) {} // Prevent delete on const data
41};
42
43// == StringBlob ==
44class StringBlob : public BlobImpl {
45 String string_;
46 virtual String string () override { return string_; }
47public:
48 explicit StringBlob (const String &name, const String &str = "");
49};
50
51StringBlob::StringBlob (const String &name, const String &str) :
52 BlobImpl (name, str.size(), NULL), string_ (str)
53{
54 data_ = string_.data();
55}
56
57// == ByteBlob ==
58template<class Deleter>
59class ByteBlob : public BlobImpl {
60 String string_;
61 Deleter deleter_;
62 virtual String string () override;
63public:
64 explicit ByteBlob (const String &name, size_t dsize, const char *data, const Deleter &deleter);
65 virtual ~ByteBlob () { deleter_ (data_); }
66};
67
68template<class Deleter>
69ByteBlob<Deleter>::ByteBlob (const String &name, size_t dsize, const char *data, const Deleter &deleter) :
70 BlobImpl (name, dsize, data), deleter_ (deleter)
71{}
72
73template<class Deleter> String
74ByteBlob<Deleter>::string ()
75{
76 if (string_.empty() && size_)
77 {
78 static std::mutex mutex;
79 std::lock_guard<std::mutex> locker (mutex);
80 if (string_.empty())
81 string_ = String (data_, size_);
82 }
83 return string_;
84}
85
86// == Blob ==
89{
90 return implp_ ? implp_->name_ : "";
91}
92
93Blob::operator bool () const
94{
95 return implp_ && implp_->size_;
96}
97
98const char*
100{
101 return implp_ ? implp_->data_ : NULL;
102}
103
104const uint8*
106{
107 return reinterpret_cast<const uint8*> (data());
108}
109
110size_t
112{
113 return implp_ ? implp_->size_ : 0;
114}
115
116String
118{
119 return implp_ ? implp_->string() : std::string();
120}
121
124
126 implp_ (blobimpl)
127{}
128
129Blob::Blob (const String &auto_url)
130{
131 if ((auto_url[0] >= 'a' && auto_url[0] <= 'z') || (auto_url[0] >= 'A' && auto_url[0] <= 'Z'))
132 {
133 size_t i = 1;
134 while ((auto_url[i] >= 'a' && auto_url[i] <= 'z') || (auto_url[i] >= 'A' && auto_url[i] <= 'Z') ||
135 // seldomly needed: auto_url[i] == '+' || auto_url[i] == '.' || auto_url[i] == '-' ||
136 (auto_url[i] >= '0' && auto_url[i] <= '9'))
137 i++;
138 if (auto_url[i] == ':')
139 {
140 // detected URL scheme
141 Blob other = from_url (auto_url);
142 implp_ = other.implp_;
143 return;
144 }
145 }
146 // assuming file path
147 Blob other = from_file (auto_url);
148 implp_ = other.implp_;
149}
150
151Blob
153{
154 const String lurl = string_tolower (url);
155 if (lurl.compare (0, 4, "res:") == 0)
156 return from_res (url.c_str() + 4);
157 if (lurl.compare (0, 5, "file:") == 0)
158 return from_file (url.c_str() + 5);
159 errno = ENOENT;
160 return Blob();
161}
162
163Blob
164Blob::from_string (const String &name, const String &data)
165{
167}
168
169static String // provides errno on error
170string_read (const String &filename, const int fd, size_t guess)
171{
172 String data;
173 if (guess)
174 data.resize (guess + 1); // pad by +1 to detect EOF reads
175 else
176 data.resize (4096); // default buffering for unknown sizes
177 size_t stored = 0;
178 for (ssize_t l = 1; l > 0; )
179 {
180 if (stored >= data.size()) // only triggered for unknown sizes
181 data.resize (2 * data.size());
182 do
183 l = read (fd, &data[stored], data.size() - stored);
184 while (l < 0 && (errno == EAGAIN || errno == EINTR));
185 stored += std::max (ssize_t (0), l);
186 if (l < 0)
187 BDEBUG ("%s: read: %s", filename, strerror (errno));
188 else
189 errno = 0;
190 }
191 data.resize (stored);
192 return data;
193}
194
195Blob
196Blob::from_file (const String &filename)
197{
198 // load blob from file
199 errno = 0;
200 const int fd = open (filename.c_str(), O_RDONLY | O_NOCTTY | O_CLOEXEC, 0);
201 struct stat sbuf = { 0, };
202 size_t file_size = 0;
203 if (fd < 0)
204 return error_result (filename, ENOENT);
205 if (fstat (fd, &sbuf) == 0 && sbuf.st_size)
206 file_size = sbuf.st_size;
207 // blob via mmap
208 void *maddr;
209 if (file_size >= 128 * 1024 &&
210 MAP_FAILED != (maddr = mmap (NULL, file_size, PROT_READ, MAP_SHARED | MAP_DENYWRITE | MAP_POPULATE, fd, 0)))
211 {
212 close (fd); // mmap keeps its own file reference
213 struct MunmapDeleter {
214 const size_t length;
215 explicit MunmapDeleter (size_t l) : length (l) {}
216 void operator() (const char *d) { munmap ((void*) d, length); }
217 };
218 return Blob (std::make_shared<ByteBlob<MunmapDeleter> > (filename, file_size, (const char*) maddr, MunmapDeleter (file_size)));
219 }
220 // blob via read
221 errno = 0;
222 String iodata = string_read (filename, fd, file_size);
223 const int saved_errno = errno;
224 close (fd);
225 errno = saved_errno;
226 if (!errno)
227 return from_string (filename, iodata);
228 // handle file errors
229 return error_result (filename, ENOENT);
230}
231
232// == zintern ==
234void
236{
237 delete[] dc_data;
238}
239
256uint8*
257zintern_decompress (unsigned int decompressed_size, const unsigned char *cdata, unsigned int cdata_size)
258{
259 uLongf dlen = decompressed_size;
260 uint64 len = dlen + 1;
261 uint8 *text = new uint8[len];
262 if (!text)
263 return NULL; // handle ENOMEM gracefully
264 int64 result = uncompress (text, &dlen, cdata, cdata_size);
265 const char *err;
266 switch (result)
267 {
268 case Z_OK:
269 if (dlen == decompressed_size)
270 {
271 err = NULL;
272 break;
273 }
274 // fall through
275 case Z_DATA_ERROR:
276 err = "internal data corruption";
277 break;
278 case Z_MEM_ERROR:
279 // err = "out of memory";
280 zintern_free (text);
281 errno = ENOMEM;
282 return NULL; // handle ENOMEM gracefully
283 case Z_BUF_ERROR:
284 err = "insufficient buffer size";
285 break;
286 default:
287 err = "unknown error";
288 break;
289 }
290 if (err)
291 {
292 zintern_free (text);
293 BDEBUG ("failed to decompress (%p, %u): %s", cdata, cdata_size, err);
295 errno = EINVAL;
296 }
297 text[dlen] = 0;
298 return text; // success
299}
300
301// == LocalResourceEntry ==
303 const char *const filename_;
304 const size_t filesize_;
305 const char *const packdata_;
306 const size_t packsize_;
307 LocalResourceEntry *const next_;
308 static LocalResourceEntry *chain_;
309public:
310 LocalResourceEntry (const char *filename, size_t filesize, const char *packdata, size_t packsize) :
311 filename_ (filename), filesize_ (filesize), packdata_ (packdata), packsize_ (packsize), next_ (chain_)
312 {
313 assert_return (next_ == chain_);
314 chain_ = this;
315 }
316};
317LocalResourceEntry *LocalResourceEntry::chain_ = NULL;
318
319// == Blob::from_res ==
320Blob
321Blob::from_res (const char *resource)
322{
323 // Find resource
324 LocalResourceEntry *entry = LocalResourceEntry::chain_;
325 while (entry)
326 if (strcmp (resource, entry->filename_) == 0)
327 break;
328 else
329 entry = entry->next_;
330 // Blobs from plain packdata_
331 if (entry &&
332 (entry->filesize_ == entry->packsize_ || // uint8[] array
333 entry->filesize_ + 1 == entry->packsize_)) // string initilization with 0-termination
334 {
335 if (entry->filesize_ + 1 == entry->packsize_)
336 assert_return (entry->packdata_[entry->filesize_] == 0, Blob());
337 return Blob (std::make_shared<ByteBlob<NoDelete>> (resource, entry->filesize_, entry->packdata_, NoDelete()));
338 }
339 else if (entry &&
340 entry->packsize_ && entry->filesize_ == 0) // variable length array with automatic size
341 return Blob (std::make_shared<ByteBlob<NoDelete>> (resource, entry->packsize_, entry->packdata_, NoDelete()));
342 // blob from compressed resources
343 if (entry && entry->packsize_ < entry->filesize_)
344 {
345 const uint8 *u8data = zintern_decompress (entry->filesize_, reinterpret_cast<const uint8*> (entry->packdata_), entry->packsize_);
346 const char *data = reinterpret_cast<const char*> (u8data);
347 struct ZinternDeleter { void operator() (const char *d) { zintern_free ((uint8*) d); } };
348 return Blob (std::make_shared<ByteBlob<ZinternDeleter>> (resource, data ? entry->filesize_ : 0, data, ZinternDeleter()));
349 }
350 // handle resource errors
351 return error_result (resource, ENOENT, String (entry ? "invalid" : "unknown") + " resource entry");
352}
353
354} // Ase
#define ENOENT
T c_str(T... args)
Binary large object storage container.
Definition blob.hh:12
static Blob from_file(const String &filename)
Create Blob by loading from filename.
Definition blob.cc:196
const uint8 * bytes()
Retrieve the Blob's data as uint8 buffer.
Definition blob.cc:105
static Blob from_url(const String &url)
Create Blob by opening a url.
Definition blob.cc:152
String name()
Retrieve the Blob's filename or url.
Definition blob.cc:88
const char * data()
Retrieve the Blob's data.
Definition blob.cc:99
size_t size()
Retrieve the Blob's data size in bytes.
Definition blob.cc:111
String string()
Copy Blob data into a zero terminated string.
Definition blob.cc:117
Blob()
Construct an empty Blob.
Definition blob.cc:122
close
T compare(T... args)
T data(T... args)
errno
fstat
stat
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
Definition internal.hh:29
#define assert_return_unreached(...)
Return from the current function and issue an assertion warning.
Definition internal.hh:31
#define CQUOTE(str)
Produce a const char* string, wrapping str into C-style double quotes.
Definition internal.hh:43
T make_shared(T... args)
T max(T... args)
mmap
munmap
The Anklang C++ API namespace.
Definition api.hh:9
uint64_t uint64
A 64-bit unsigned integer, use PRI*64 in format strings.
Definition cxxaux.hh:25
String string_tolower(const String &str)
Convert all string characters into Unicode lower case characters.
Definition strings.cc:136
uint8_t uint8
An 8-bit unsigned integer.
Definition cxxaux.hh:22
int64_t int64
A 64-bit unsigned integer, use PRI*64 in format strings.
Definition cxxaux.hh:29
uint8 * zintern_decompress(unsigned int decompressed_size, const unsigned char *cdata, unsigned int cdata_size)
Definition blob.cc:257
std::string String
Convenience alias for std::string.
Definition cxxaux.hh:35
void zintern_free(uint8 *dc_data)
Free data returned from zintern_decompress().
Definition blob.cc:235
open
read