summaryrefslogtreecommitdiff
path: root/utils/InMemoryFile.cc
blob: c34bd113ce91f7342ab234f68867f521f9ce9b46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//========================================================================
//
// InMemoryFile.cc
//
// Represents a file in-memory with GNU's stdio wrappers.
// NOTE as of this writing, open() depends on the glibc 'fopencookie'
// extension and is not supported on other platforms. The
// HAVE_IN_MEMORY_FILE macro is intended to reflect whether this class is
// usable.
//
// This file is licensed under the GPLv2 or later
//
// Copyright (C) 2018, 2019 Greg Knight <lyngvi@gmail.com>
//
//========================================================================

#include "InMemoryFile.h"

#include <cstring>
#include <sstream>

InMemoryFile::InMemoryFile()
    : iohead(0)
    , fptr(nullptr)
{
}

#ifdef HAVE_IN_MEMORY_FILE_FOPENCOOKIE
ssize_t InMemoryFile::_read(char* buf, size_t sz)
{
    auto toRead = std::min<size_t>(data.size() - iohead, sz);
    memcpy(&buf[0], &data[iohead], toRead);
    iohead += toRead;
    return toRead;
}

ssize_t InMemoryFile::_write(const char* buf, size_t sz)
{
    if (iohead + sz > data.size())
        data.resize(iohead + sz);
    memcpy(&data[iohead], buf, sz);
    iohead += sz;
    return sz;        
}

int InMemoryFile::_seek(off64_t* offset, int whence)
{
    switch (whence) {
        case SEEK_SET: iohead  = (*offset); break;
        case SEEK_CUR: iohead += (*offset); break;
        case SEEK_END: iohead -= (*offset); break;
    }
    (*offset) = std::min<off64_t>(std::max<off64_t>(iohead, 0l), data.size());
    iohead = static_cast<size_t>(*offset);
    return 0;
}
#endif // def HAVE_IN_MEMORY_FILE_FOPENCOOKIE

FILE* InMemoryFile::open(const char* mode)
{
#ifdef HAVE_IN_MEMORY_FILE_FOPENCOOKIE
    if (fptr != nullptr) {
        fprintf(stderr, "InMemoryFile: BUG: Why is this opened more than once?");
        return nullptr; // maybe there's some legit reason for it, whoever comes up with one can remove this line
    }
    static cookie_io_functions_t methods = {
        /* .read = */ [](void* self, char* buf, size_t sz) { return ((InMemoryFile*)self)->_read(buf, sz); },
        /* .write = */ [](void* self, const char* buf, size_t sz) { return ((InMemoryFile*)self)->_write(buf, sz); },
        /* .seek = */ [](void* self, off64_t* offset, int whence) { return ((InMemoryFile*)self)->_seek(offset, whence); },
        /* .close = */ [](void* self) { ((InMemoryFile*)self)->fptr = nullptr; return 0; },
    };
    return fptr = fopencookie(this, mode, methods);
#else
    fprintf (stderr, "If you can read this, your platform does not support the features necessary to achieve your goals.");
    return nullptr;
#endif
}