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 | |
23 | InMemoryFile::InMemoryFile() = default; |
24 | |
25 | #ifdef HAVE_IN_MEMORY_FILE_FOPENCOOKIE |
26 | ssize_t InMemoryFile::_read(char *buf, size_t sz) |
27 | { |
28 | auto toRead = std::min<size_t>(a: data.size() - iohead, b: sz); |
29 | memcpy(dest: &buf[0], src: &data[iohead], n: toRead); |
30 | iohead += toRead; |
31 | return toRead; |
32 | } |
33 | |
34 | ssize_t InMemoryFile::_write(const char *buf, size_t sz) |
35 | { |
36 | if (iohead + sz > data.size()) { |
37 | data.resize(new_size: iohead + sz); |
38 | } |
39 | memcpy(dest: &data[iohead], src: buf, n: sz); |
40 | iohead += sz; |
41 | return sz; |
42 | } |
43 | |
44 | int InMemoryFile::_seek(off64_t *offset, int whence) |
45 | { |
46 | switch (whence) { |
47 | case SEEK_SET: |
48 | iohead = (*offset); |
49 | break; |
50 | case SEEK_CUR: |
51 | iohead += (*offset); |
52 | break; |
53 | case SEEK_END: |
54 | iohead -= (*offset); |
55 | break; |
56 | } |
57 | (*offset) = std::min<off64_t>(a: std::max<off64_t>(a: iohead, b: 0l), b: data.size()); |
58 | iohead = static_cast<size_t>(*offset); |
59 | return 0; |
60 | } |
61 | #endif // def HAVE_IN_MEMORY_FILE_FOPENCOOKIE |
62 | |
63 | FILE *InMemoryFile::open(const char *mode) |
64 | { |
65 | #ifdef HAVE_IN_MEMORY_FILE_FOPENCOOKIE |
66 | if (fptr != nullptr) { |
67 | fprintf(stderr, format: "InMemoryFile: BUG: Why is this opened more than once?" ); |
68 | return nullptr; // maybe there's some legit reason for it, whoever comes up with one can remove this line |
69 | } |
70 | static const cookie_io_functions_t methods = { |
71 | /* .read = */ [](void *self, char *buf, size_t sz) { return ((InMemoryFile *)self)->_read(buf, sz); }, |
72 | /* .write = */ [](void *self, const char *buf, size_t sz) { return ((InMemoryFile *)self)->_write(buf, sz); }, |
73 | /* .seek = */ [](void *self, off64_t *offset, int whence) { return ((InMemoryFile *)self)->_seek(offset, whence); }, |
74 | /* .close = */ |
75 | [](void *self) { |
76 | ((InMemoryFile *)self)->fptr = nullptr; |
77 | return 0; |
78 | }, |
79 | }; |
80 | return fptr = fopencookie(magic_cookie: this, modes: mode, io_funcs: methods); |
81 | #else |
82 | fprintf(stderr, "If you can read this, your platform does not support the features necessary to achieve your goals." ); |
83 | return nullptr; |
84 | #endif |
85 | } |
86 | |