1 //========================================================================
2 //
3 // InMemoryFile.cc
4 //
5 // Represents a file in-memory with GNU's stdio wrappers.
6 // NOTE as of this writing, open() depends on the glibc 'fopencookie'
7 // extension and is not supported on other platforms. The
8 // HAVE_IN_MEMORY_FILE macro is intended to reflect whether this class is
9 // usable.
10 //
11 // This file is licensed under the GPLv2 or later
12 //
13 // Copyright (C) 2018, 2019 Greg Knight <lyngvi@gmail.com>
14 // Copyright (C) 2020 Albert Astals Cid <aacid@kde.org>
15 //
16 //========================================================================
17
18 #include "InMemoryFile.h"
19
20 #include <cstring>
21 #include <sstream>
22
InMemoryFile()23 InMemoryFile::InMemoryFile() : iohead(0), fptr(nullptr) { }
24
25 #ifdef HAVE_IN_MEMORY_FILE_FOPENCOOKIE
_read(char * buf,size_t sz)26 ssize_t InMemoryFile::_read(char *buf, size_t sz)
27 {
28 auto toRead = std::min<size_t>(data.size() - iohead, sz);
29 memcpy(&buf[0], &data[iohead], toRead);
30 iohead += toRead;
31 return toRead;
32 }
33
_write(const char * buf,size_t sz)34 ssize_t InMemoryFile::_write(const char *buf, size_t sz)
35 {
36 if (iohead + sz > data.size())
37 data.resize(iohead + sz);
38 memcpy(&data[iohead], buf, sz);
39 iohead += sz;
40 return sz;
41 }
42
_seek(off64_t * offset,int whence)43 int InMemoryFile::_seek(off64_t *offset, int whence)
44 {
45 switch (whence) {
46 case SEEK_SET:
47 iohead = (*offset);
48 break;
49 case SEEK_CUR:
50 iohead += (*offset);
51 break;
52 case SEEK_END:
53 iohead -= (*offset);
54 break;
55 }
56 (*offset) = std::min<off64_t>(std::max<off64_t>(iohead, 0l), data.size());
57 iohead = static_cast<size_t>(*offset);
58 return 0;
59 }
60 #endif // def HAVE_IN_MEMORY_FILE_FOPENCOOKIE
61
open(const char * mode)62 FILE *InMemoryFile::open(const char *mode)
63 {
64 #ifdef HAVE_IN_MEMORY_FILE_FOPENCOOKIE
65 if (fptr != nullptr) {
66 fprintf(stderr, "InMemoryFile: BUG: Why is this opened more than once?");
67 return nullptr; // maybe there's some legit reason for it, whoever comes up with one can remove this line
68 }
69 static const cookie_io_functions_t methods = {
70 /* .read = */ [](void *self, char *buf, size_t sz) { return ((InMemoryFile *)self)->_read(buf, sz); },
71 /* .write = */ [](void *self, const char *buf, size_t sz) { return ((InMemoryFile *)self)->_write(buf, sz); },
72 /* .seek = */ [](void *self, off64_t *offset, int whence) { return ((InMemoryFile *)self)->_seek(offset, whence); },
73 /* .close = */
74 [](void *self) {
75 ((InMemoryFile *)self)->fptr = nullptr;
76 return 0;
77 },
78 };
79 return fptr = fopencookie(this, mode, methods);
80 #else
81 fprintf(stderr, "If you can read this, your platform does not support the features necessary to achieve your goals.");
82 return nullptr;
83 #endif
84 }
85