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