implement Node interface to System/NativeFile
and remove some useless headers
This commit is contained in:
parent
cec3f79321
commit
1713b11ac8
@ -45,10 +45,7 @@ target_sources(nf7
|
||||
nf7.hh
|
||||
|
||||
common/aggregate_command.hh
|
||||
common/async_buffer.hh
|
||||
common/async_buffer_adaptor.hh
|
||||
common/audio_queue.hh
|
||||
common/buffer.hh
|
||||
common/dir.hh
|
||||
common/dir_item.hh
|
||||
common/file_base.hh
|
||||
@ -74,7 +71,6 @@ target_sources(nf7
|
||||
common/gui_window.hh
|
||||
common/history.hh
|
||||
common/life.hh
|
||||
common/lock.hh
|
||||
common/logger.hh
|
||||
common/logger_ref.hh
|
||||
common/luajit.hh
|
||||
@ -94,7 +90,6 @@ target_sources(nf7
|
||||
common/queue.hh
|
||||
common/sequencer.hh
|
||||
common/squashed_history.hh
|
||||
common/task.hh
|
||||
common/thread.hh
|
||||
common/timed_queue.hh
|
||||
common/util_string.hh
|
||||
|
@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/buffer.hh"
|
||||
#include "common/future.hh"
|
||||
#include "common/lock.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class AsyncBuffer : public nf7::File::Interface, public nf7::Lock::Resource {
|
||||
public:
|
||||
using IOException = Buffer::IOException;
|
||||
|
||||
AsyncBuffer() = default;
|
||||
AsyncBuffer(const AsyncBuffer&) = delete;
|
||||
AsyncBuffer(AsyncBuffer&&) = delete;
|
||||
AsyncBuffer& operator=(const AsyncBuffer&) = delete;
|
||||
AsyncBuffer& operator=(AsyncBuffer&&) = delete;
|
||||
|
||||
virtual nf7::Future<size_t> Read(size_t offset, uint8_t* buf, size_t size) noexcept = 0;
|
||||
virtual nf7::Future<size_t> Write(size_t offset, const uint8_t* buf, size_t size) noexcept = 0;
|
||||
virtual nf7::Future<size_t> Truncate(size_t) noexcept = 0;
|
||||
|
||||
virtual nf7::Future<size_t> size() const noexcept = 0;
|
||||
virtual Buffer::Flags flags() const noexcept = 0;
|
||||
|
||||
virtual std::shared_ptr<AsyncBuffer> self(AsyncBuffer* = nullptr) noexcept = 0;
|
||||
|
||||
protected:
|
||||
using nf7::Lock::Resource::OnLock;
|
||||
using nf7::Lock::Resource::OnUnlock;
|
||||
};
|
||||
|
||||
} // namespace nf7
|
@ -1,105 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/async_buffer.hh"
|
||||
#include "common/buffer.hh"
|
||||
#include "common/future.hh"
|
||||
#include "common/queue.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class AsyncBufferAdaptor final :
|
||||
public nf7::AsyncBuffer, public std::enable_shared_from_this<AsyncBufferAdaptor> {
|
||||
public:
|
||||
AsyncBufferAdaptor(const std::shared_ptr<nf7::Context>& ctx,
|
||||
const std::shared_ptr<nf7::Buffer>& buf) noexcept :
|
||||
ctx_(ctx), buf_(buf) {
|
||||
}
|
||||
|
||||
nf7::Future<size_t> Read(size_t offset, uint8_t* ptr, size_t size) noexcept override {
|
||||
nf7::Future<size_t>::Promise pro(ctx_);
|
||||
Exec([pro, buf = buf_, offset, ptr, size]() mutable {
|
||||
pro.Wrap([&]() { return buf->Read(offset, ptr, size); });
|
||||
});
|
||||
return pro.future();
|
||||
}
|
||||
nf7::Future<size_t> Write(size_t offset, const uint8_t* ptr, size_t size) noexcept override {
|
||||
nf7::Future<size_t>::Promise pro(ctx_);
|
||||
Exec([pro, buf = buf_, offset, ptr, size]() mutable {
|
||||
pro.Wrap([&]() { return buf->Write(offset, ptr, size); });
|
||||
});
|
||||
return pro.future();
|
||||
}
|
||||
nf7::Future<size_t> Truncate(size_t size) noexcept override {
|
||||
nf7::Future<size_t>::Promise pro(ctx_);
|
||||
Exec([pro, buf = buf_, size]() mutable {
|
||||
pro.Wrap([&]() { return buf->Truncate(size); });
|
||||
});
|
||||
return pro.future();
|
||||
}
|
||||
|
||||
nf7::Future<size_t> size() const noexcept override {
|
||||
nf7::Future<size_t>::Promise pro(ctx_);
|
||||
const_cast<AsyncBufferAdaptor&>(*this).Exec([pro, buf = buf_]() mutable {
|
||||
pro.Wrap([&]() { return buf->size(); });
|
||||
});
|
||||
return pro.future();
|
||||
}
|
||||
Buffer::Flags flags() const noexcept override {
|
||||
return buf_->flags();
|
||||
}
|
||||
|
||||
std::shared_ptr<AsyncBuffer> self(AsyncBuffer*) noexcept override {
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
protected:
|
||||
void OnLock() noexcept override {
|
||||
Exec([buf = buf_]() { return buf->Lock(); });
|
||||
}
|
||||
void OnUnlock() noexcept override {
|
||||
Exec([buf = buf_]() { return buf->Unlock(); });
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<nf7::Context> ctx_;
|
||||
std::shared_ptr<nf7::Buffer> buf_;
|
||||
|
||||
std::mutex mtx_;
|
||||
bool working_ = false;
|
||||
nf7::Queue<std::function<void()>> q_;
|
||||
|
||||
void Exec(std::function<void()>&& f) noexcept {
|
||||
q_.Push(std::move(f));
|
||||
|
||||
std::unique_lock<std::mutex> k(mtx_);
|
||||
if (!std::exchange(working_, true)) {
|
||||
ctx_->env().ExecAsync(
|
||||
ctx_, [self = shared_from_this()]() { self->Handle(); });
|
||||
}
|
||||
}
|
||||
void Handle() noexcept {
|
||||
std::unique_lock<std::mutex> k(mtx_);
|
||||
if (auto task = q_.Pop()) {
|
||||
k.unlock();
|
||||
try {
|
||||
(*task)();
|
||||
} catch (nf7::Exception&) {
|
||||
// TODO: unhandled exception :(
|
||||
}
|
||||
ctx_->env().ExecAsync(
|
||||
ctx_, [self = shared_from_this()]() { self->Handle(); });
|
||||
} else {
|
||||
working_ = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace nf7
|
@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class Buffer {
|
||||
public:
|
||||
enum Flag : uint8_t {
|
||||
kRead = 1 << 0,
|
||||
kWrite = 1 << 1,
|
||||
};
|
||||
using Flags = uint8_t;
|
||||
|
||||
class IOException;
|
||||
|
||||
Buffer() = default;
|
||||
virtual ~Buffer() = default;
|
||||
Buffer(const Buffer&) = delete;
|
||||
Buffer(Buffer&&) = delete;
|
||||
Buffer& operator=(const Buffer&) = delete;
|
||||
Buffer& operator=(Buffer&&) = delete;
|
||||
|
||||
virtual void Lock() = 0;
|
||||
virtual void Unlock() = 0;
|
||||
virtual size_t Read(size_t offset, uint8_t* buf, size_t size) = 0;
|
||||
virtual size_t Write(size_t offset, const uint8_t* buf, size_t size) = 0;
|
||||
virtual size_t Truncate(size_t size) = 0;
|
||||
|
||||
virtual size_t size() const = 0;
|
||||
virtual Flags flags() const noexcept = 0;
|
||||
};
|
||||
|
||||
class Buffer::IOException : public nf7::Exception {
|
||||
public:
|
||||
using Exception::Exception;
|
||||
};
|
||||
|
||||
} // namespace nf7
|
112
common/lock.hh
112
common/lock.hh
@ -1,112 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <coroutine>
|
||||
#include <deque>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/future.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class Lock final {
|
||||
public:
|
||||
class Resource;
|
||||
class Exception : public nf7::Exception {
|
||||
public:
|
||||
using nf7::Exception::Exception;
|
||||
};
|
||||
|
||||
Lock() = default;
|
||||
Lock(Resource& res, bool ex) noexcept : res_(&res), ex_(ex) {
|
||||
}
|
||||
inline ~Lock() noexcept;
|
||||
Lock(const Lock&) = delete;
|
||||
Lock(Lock&&) = delete;
|
||||
Lock& operator=(const Lock&) = delete;
|
||||
Lock& operator=(Lock&&) = delete;
|
||||
|
||||
void Validate() const {
|
||||
if (!res_) throw Lock::Exception("target expired");
|
||||
}
|
||||
|
||||
private:
|
||||
Resource* res_ = nullptr;
|
||||
bool ex_ = false;
|
||||
};
|
||||
|
||||
class Lock::Resource {
|
||||
public:
|
||||
friend Lock;
|
||||
|
||||
Resource() = default;
|
||||
virtual ~Resource() noexcept {
|
||||
if (auto lock = lock_.lock()) {
|
||||
lock->res_ = nullptr;
|
||||
}
|
||||
for (auto pend : pends_) {
|
||||
pend.pro.Throw(std::make_exception_ptr<Lock::Exception>({"lock cancelled"}));
|
||||
}
|
||||
}
|
||||
Resource(const Resource&) = delete;
|
||||
Resource(Resource&&) = delete;
|
||||
Resource& operator=(const Resource&) = delete;
|
||||
Resource& operator=(Resource&&) = delete;
|
||||
|
||||
nf7::Future<std::shared_ptr<Lock>> AcquireLock(bool ex) noexcept {
|
||||
if (auto ret = TryAcquireLock(ex)) return ret;
|
||||
|
||||
if (ex || pends_.empty() || pends_.back().ex) {
|
||||
pends_.push_back(ex);
|
||||
}
|
||||
return pends_.back().pro.future();
|
||||
}
|
||||
std::shared_ptr<Lock> TryAcquireLock(bool ex) noexcept {
|
||||
if (auto k = lock_.lock()) {
|
||||
return !ex && !k->ex_ && pends_.empty()? k: nullptr;
|
||||
}
|
||||
auto k = std::make_shared<Lock>(*this, ex);
|
||||
lock_ = k;
|
||||
OnLock();
|
||||
return k;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void OnLock() noexcept { }
|
||||
virtual void OnUnlock() noexcept { }
|
||||
|
||||
private:
|
||||
struct Pending final {
|
||||
public:
|
||||
Pending(bool ex_) noexcept : ex(ex_) { }
|
||||
|
||||
bool ex;
|
||||
nf7::Future<std::shared_ptr<Lock>>::Promise pro;
|
||||
};
|
||||
std::weak_ptr<Lock> lock_;
|
||||
std::deque<Pending> pends_;
|
||||
};
|
||||
|
||||
|
||||
Lock::~Lock() noexcept {
|
||||
if (!res_) return;
|
||||
if (res_->pends_.empty()) {
|
||||
res_->OnUnlock();
|
||||
return;
|
||||
}
|
||||
|
||||
auto next = std::move(res_->pends_.front());
|
||||
res_->pends_.pop_front();
|
||||
|
||||
auto lock = std::make_shared<Lock>(*res_, next.ex);
|
||||
res_->lock_ = lock;
|
||||
next.pro.Return(std::move(lock));
|
||||
}
|
||||
|
||||
} // namespace nf7
|
@ -1,13 +1,10 @@
|
||||
#include "common/luajit_thread.hh"
|
||||
#include "common/luajit_thread_lambda.hh"
|
||||
#include "common/luajit_thread_lock.hh"
|
||||
|
||||
#include <chrono>
|
||||
#include <sstream>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/async_buffer.hh"
|
||||
|
||||
|
||||
namespace nf7::luajit {
|
||||
|
||||
@ -121,13 +118,7 @@ static void PushMeta(lua_State* L) noexcept {
|
||||
th->env().ExecSub(th->ctx(), [th, L, id, iface = std::move(iface)]() {
|
||||
try {
|
||||
auto& f = th->env().GetFileOrThrow(static_cast<nf7::File::Id>(id));
|
||||
if (iface == "buffer") {
|
||||
Thread::Lock<nf7::AsyncBuffer>::AcquireAndPush(L, th, f, false);
|
||||
} else if (iface == "exbuffer") {
|
||||
Thread::Lock<nf7::AsyncBuffer>::AcquireAndPush(L, th, f, true);
|
||||
} else if (iface == "lua") {
|
||||
// WIP GetLuaObjAndPush(L, th, f);
|
||||
} else if (iface == "node") {
|
||||
if (iface == "node") {
|
||||
Thread::Lambda::CreateAndPush(L, th, f);
|
||||
} else {
|
||||
throw nf7::Exception {"unknown interface: "+iface};
|
||||
|
@ -1,190 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/luajit_thread.hh"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/async_buffer.hh"
|
||||
#include "common/lock.hh"
|
||||
#include "common/luajit.hh"
|
||||
|
||||
|
||||
namespace nf7::luajit {
|
||||
|
||||
template <typename T>
|
||||
class Thread::Lock final : public Thread::RegistryItem,
|
||||
public std::enable_shared_from_this<Thread::Lock<T>> {
|
||||
public:
|
||||
using Res = T;
|
||||
|
||||
static void AcquireAndPush(
|
||||
lua_State* L, const std::shared_ptr<Thread>& th, nf7::File& f, bool ex) {
|
||||
auto res = f.interfaceOrThrow<Res>().self();
|
||||
res->AcquireLock(ex).Then([L, th, res](auto fu) {
|
||||
try {
|
||||
auto k = std::make_shared<Thread::Lock<Res>>(th, res, fu.value());
|
||||
th->ljq()->Push(th->ctx(), [L, th, k](auto) {
|
||||
th->Register(L, k);
|
||||
k->Push(L);
|
||||
th->Resume(L, 1);
|
||||
});
|
||||
} catch (nf7::Exception& e) {
|
||||
th->ExecResume(L, nullptr, e.msg());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Lock(const std::shared_ptr<Thread>& th,
|
||||
const std::shared_ptr<Res>& res,
|
||||
const std::shared_ptr<nf7::Lock>& lock) :
|
||||
th_(th), res_(res), lock_(lock) {
|
||||
}
|
||||
|
||||
void Push(lua_State* L) noexcept {
|
||||
luajit::PushWeakPtr<Thread::Lock<Res>>(L, Thread::Lock<T>::shared_from_this());
|
||||
PushMeta(L);
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
|
||||
private:
|
||||
std::weak_ptr<Thread> th_;
|
||||
|
||||
std::shared_ptr<Res> res_;
|
||||
std::shared_ptr<nf7::Lock> lock_;
|
||||
|
||||
|
||||
auto Validate(lua_State* L) {
|
||||
auto t = th_.lock();
|
||||
if (!t) {
|
||||
luaL_error(L, "thread expired");
|
||||
}
|
||||
t->EnsureActive(L);
|
||||
try {
|
||||
lock_->Validate();
|
||||
} catch (nf7::Exception& e) {
|
||||
luaL_error(L, "%s", e.msg().c_str());
|
||||
}
|
||||
return std::make_tuple(t, res_, lock_);
|
||||
}
|
||||
|
||||
static void PushMeta(lua_State* L) noexcept;
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
void Thread::Lock<nf7::AsyncBuffer>::PushMeta(lua_State* L) noexcept {
|
||||
constexpr const char* kCustomTypeName =
|
||||
"nf7::luajit::Thread::Lock<nf7::AsyncBuffer>";
|
||||
|
||||
constexpr size_t kBufferSizeMax = 1024 * 1024 * 64;
|
||||
|
||||
if (luaL_newmetatable(L, kCustomTypeName)) {
|
||||
lua_createtable(L, 0, 0);
|
||||
|
||||
// lock:read(offset, bytes [, mutable vector]) -> MutableVector
|
||||
lua_pushcfunction(L, ([](auto L) {
|
||||
auto [th, buf, lock] = CheckWeakPtr<Lock>(L, 1, kCustomTypeName)->Validate(L);
|
||||
|
||||
auto off = luaL_checkinteger(L, 2);
|
||||
auto size = luaL_checkinteger(L, 3);
|
||||
|
||||
if (off < 0) {
|
||||
return luaL_error(L, "negative offset");
|
||||
}
|
||||
if (size < 0) {
|
||||
return luaL_error(L, "negative size");
|
||||
}
|
||||
|
||||
const size_t usize = static_cast<size_t>(size);
|
||||
if (usize > kBufferSizeMax) {
|
||||
return luaL_error(L, "too large size is requested");
|
||||
}
|
||||
|
||||
// allocates new vector to store result or reuses the passed vector
|
||||
std::shared_ptr<std::vector<uint8_t>> vec;
|
||||
if (auto src = ToMutableVector(L, 4)) {
|
||||
vec = std::make_shared<std::vector<uint8_t>>(std::move(*src));
|
||||
vec->resize(static_cast<size_t>(size));
|
||||
} else {
|
||||
vec = std::make_shared<std::vector<uint8_t>>(size);
|
||||
}
|
||||
|
||||
buf->Read(static_cast<size_t>(off), vec->data(), usize).
|
||||
Then([th, L, vec](auto fu) {
|
||||
try {
|
||||
vec->resize(fu.value());
|
||||
th->ExecResume(L, std::move(*vec));
|
||||
} catch (nf7::Exception& e) {
|
||||
th->ExecResume(L, std::vector<uint8_t> {}, e.msg());
|
||||
}
|
||||
});
|
||||
th->ExpectYield(L);
|
||||
return lua_yield(L, 0);
|
||||
}));
|
||||
lua_setfield(L, -2, "read");
|
||||
|
||||
// lock:write(offset, vector) -> size
|
||||
lua_pushcfunction(L, ([](auto L) {
|
||||
auto [th, buf, lock] = CheckWeakPtr<Lock>(L, 1, kCustomTypeName)->Validate(L);
|
||||
|
||||
auto off = luaL_checkinteger(L, 2);
|
||||
auto optvec = luajit::ToVector(L, 3);
|
||||
|
||||
if (off < 0) {
|
||||
return luaL_error(L, "negative offset");
|
||||
}
|
||||
if (!optvec) {
|
||||
return luaL_error(L, "vector is expected for the third argument");
|
||||
}
|
||||
auto& vec = *optvec;
|
||||
|
||||
buf->Write(static_cast<size_t>(off), vec->data(), vec->size()).
|
||||
Then([th, L, vec](auto fu) {
|
||||
try {
|
||||
th->ExecResume(L, fu.value());
|
||||
} catch (nf7::Exception& e) {
|
||||
th->ExecResume(L, 0, e.msg());
|
||||
}
|
||||
});
|
||||
th->ExpectYield(L);
|
||||
return lua_yield(L, 0);
|
||||
}));
|
||||
lua_setfield(L, -2, "write");
|
||||
|
||||
// lock:truncate(size) -> size
|
||||
lua_pushcfunction(L, ([](auto L) {
|
||||
auto [th, buf, lock] = CheckWeakPtr<Lock>(L, 1, kCustomTypeName)->Validate(L);
|
||||
|
||||
auto size = luaL_checkinteger(L, 2);
|
||||
if (size < 0) {
|
||||
return luaL_error(L, "negative size");
|
||||
}
|
||||
|
||||
buf->Truncate(static_cast<size_t>(size)).
|
||||
Then([th, L](auto fu) {
|
||||
try {
|
||||
th->ExecResume(L, fu.value());
|
||||
} catch (nf7::Exception& e) {
|
||||
th->ExecResume(L, nullptr, e.msg());
|
||||
}
|
||||
});
|
||||
th->ExpectYield(L);
|
||||
return lua_yield(L, 0);
|
||||
}));
|
||||
lua_setfield(L, -2, "truncate");
|
||||
|
||||
// lock:unlock()
|
||||
luajit::PushWeakPtrDeleter<Thread::Lock<Res>>(L);
|
||||
lua_setfield(L, -2, "unlock");
|
||||
|
||||
lua_setfield(L, -2, "__index");
|
||||
|
||||
luajit::PushWeakPtrDeleter<Lock>(L);
|
||||
lua_setfield(L, -2, "__gc");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nf7::luajit
|
@ -8,54 +8,48 @@
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/buffer.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class NativeFile final : public nf7::Buffer, public nf7::Context {
|
||||
class NativeFile final : public nf7::Context {
|
||||
public:
|
||||
class Exception final : public nf7::Exception {
|
||||
using nf7::Exception::Exception;
|
||||
};
|
||||
|
||||
enum Flag : uint8_t {
|
||||
kCreateIf = 1 << 0,
|
||||
kExclusive = 1 << 1,
|
||||
kRead = 1 << 0,
|
||||
kWrite = 1 << 1,
|
||||
};
|
||||
using Flags = uint8_t;
|
||||
|
||||
NativeFile() = delete;
|
||||
NativeFile(nf7::File& f,
|
||||
const std::filesystem::path& path,
|
||||
Buffer::Flags flags,
|
||||
Flags nflags) noexcept :
|
||||
Context(f.env(), f.id()), path_(path), flags_(flags), nflags_(nflags) {
|
||||
NativeFile(nf7::Env& env, nf7::File::Id id,
|
||||
const std::filesystem::path& path, Flags flags) :
|
||||
Context(env, id), path_(path), flags_(flags) {
|
||||
Init();
|
||||
}
|
||||
~NativeFile() noexcept;
|
||||
NativeFile(const NativeFile&) = delete;
|
||||
NativeFile(NativeFile&&) = delete;
|
||||
NativeFile& operator=(const NativeFile&) = delete;
|
||||
NativeFile& operator=(NativeFile&&) = delete;
|
||||
|
||||
void Lock() override;
|
||||
void Unlock() override;
|
||||
size_t Read(size_t offset, uint8_t* buf, size_t size) override;
|
||||
size_t Write(size_t offset, const uint8_t* buf, size_t size) override;
|
||||
size_t Truncate(size_t size) override;
|
||||
size_t Read(size_t offset, uint8_t* buf, size_t size);
|
||||
size_t Write(size_t offset, const uint8_t* buf, size_t size);
|
||||
size_t Truncate(size_t size);
|
||||
|
||||
size_t size() const override;
|
||||
Buffer::Flags flags() const noexcept override {
|
||||
Flags flags() const noexcept {
|
||||
return flags_;
|
||||
}
|
||||
|
||||
void CleanUp() noexcept override;
|
||||
void Abort() noexcept override;
|
||||
|
||||
size_t GetMemoryUsage() const noexcept override;
|
||||
std::string GetDescription() const noexcept override;
|
||||
|
||||
private:
|
||||
const std::filesystem::path path_;
|
||||
const Buffer::Flags flags_;
|
||||
const NativeFile::Flags nflags_;
|
||||
const Flags flags_;
|
||||
|
||||
std::optional<uintptr_t> handle_;
|
||||
uintptr_t handle_;
|
||||
|
||||
void Init();
|
||||
};
|
||||
|
||||
} // namespace nf7
|
||||
|
@ -11,117 +11,59 @@ extern "C" {
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
void NativeFile::Lock() {
|
||||
if (handle_) {
|
||||
throw nf7::Buffer::IOException("already locked");
|
||||
}
|
||||
|
||||
void NativeFile::Init() {
|
||||
int flags = 0;
|
||||
if ((flags_ & nf7::Buffer::kRead) && (flags_ & nf7::Buffer::kWrite)) {
|
||||
flags |= O_RDWR;
|
||||
} else if (flags_ & nf7::Buffer::kRead) {
|
||||
if ((flags_ & kRead) && (flags_ & kWrite)) {
|
||||
flags |= O_RDWR | O_CREAT;
|
||||
} else if (flags_ & kRead) {
|
||||
flags |= O_RDONLY;
|
||||
} else if (flags_ & nf7::Buffer::kWrite) {
|
||||
flags |= O_WRONLY;
|
||||
} else if (flags_ & kWrite) {
|
||||
flags |= O_WRONLY | O_CREAT;
|
||||
}
|
||||
if (nflags_ & kCreateIf) flags |= O_CREAT;
|
||||
|
||||
int fd = open(path_.string().c_str(), flags, 0600);
|
||||
if (fd < 0) {
|
||||
throw nf7::Buffer::IOException("open failure");
|
||||
throw NativeFile::Exception {"open failure"};
|
||||
}
|
||||
handle_ = static_cast<uint64_t>(fd);
|
||||
|
||||
if (nflags_ & kExclusive) {
|
||||
if (flock(fd, LOCK_EX) != 0) {
|
||||
close(fd);
|
||||
throw nf7::Buffer::IOException("flock failure");
|
||||
}
|
||||
}
|
||||
}
|
||||
void NativeFile::Unlock() {
|
||||
if (!handle_) {
|
||||
throw nf7::Buffer::IOException("not locked yet");
|
||||
}
|
||||
const auto fd = static_cast<int>(*handle_);
|
||||
if (nflags_ & kExclusive) {
|
||||
if (flock(fd, LOCK_UN) != 0) {
|
||||
close(fd);
|
||||
throw nf7::Buffer::IOException("flock failure");
|
||||
}
|
||||
}
|
||||
NativeFile::~NativeFile() noexcept {
|
||||
const auto fd = static_cast<int>(handle_);
|
||||
if (close(fd) == -1) {
|
||||
throw nf7::Buffer::IOException("close failure");
|
||||
// ;(
|
||||
}
|
||||
handle_ = std::nullopt;
|
||||
}
|
||||
|
||||
size_t NativeFile::Read(size_t offset, uint8_t* buf, size_t size) {
|
||||
if (!handle_) {
|
||||
throw nf7::Buffer::IOException("not locked yet");
|
||||
}
|
||||
const auto fd = static_cast<int>(*handle_);
|
||||
const auto fd = static_cast<int>(handle_);
|
||||
const auto off = static_cast<off_t>(offset);
|
||||
if (lseek(fd, off, SEEK_SET) == off-1) {
|
||||
throw nf7::Buffer::IOException("lseek failure");
|
||||
throw NativeFile::Exception {"lseek failure"};
|
||||
}
|
||||
const auto ret = read(fd, buf, size);
|
||||
if (ret == -1) {
|
||||
throw nf7::Buffer::IOException("read failure");
|
||||
throw NativeFile::Exception {"read failure"};
|
||||
}
|
||||
return static_cast<size_t>(ret);
|
||||
}
|
||||
size_t NativeFile::Write(size_t offset, const uint8_t* buf, size_t size) {
|
||||
if (!handle_) {
|
||||
throw nf7::Buffer::IOException("not locked yet");
|
||||
}
|
||||
const auto fd = static_cast<int>(*handle_);
|
||||
const auto fd = static_cast<int>(handle_);
|
||||
const auto off = static_cast<off_t>(offset);
|
||||
if (lseek(fd, off, SEEK_SET) == off-1) {
|
||||
throw nf7::Buffer::IOException("lseek failure");
|
||||
throw nf7::NativeFile::Exception {"lseek failure"};
|
||||
}
|
||||
const auto ret = write(fd, buf, size);
|
||||
if (ret == -1) {
|
||||
throw nf7::Buffer::IOException("write failure");
|
||||
throw nf7::NativeFile::Exception {"write failure"};
|
||||
}
|
||||
return static_cast<size_t>(ret);
|
||||
}
|
||||
size_t NativeFile::Truncate(size_t size) {
|
||||
if (!handle_) {
|
||||
throw nf7::Buffer::IOException("not locked yet");
|
||||
}
|
||||
const auto fd = static_cast<int>(*handle_);
|
||||
const auto fd = static_cast<int>(handle_);
|
||||
if (ftruncate(fd, static_cast<off_t>(size)) == 0) {
|
||||
throw nf7::Buffer::IOException("ftruncate failure");
|
||||
throw nf7::NativeFile::Exception {"ftruncate failure"};
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t NativeFile::size() const {
|
||||
if (!handle_) {
|
||||
throw nf7::Buffer::IOException("not locked yet");
|
||||
}
|
||||
const auto fd = static_cast<int>(*handle_);
|
||||
const auto ret = lseek(fd, 0, SEEK_END);
|
||||
if (ret == -1) {
|
||||
throw nf7::Buffer::IOException("lseek failure");
|
||||
}
|
||||
return static_cast<size_t>(ret);
|
||||
}
|
||||
|
||||
void NativeFile::CleanUp() noexcept {
|
||||
}
|
||||
void NativeFile::Abort() noexcept {
|
||||
}
|
||||
size_t NativeFile::GetMemoryUsage() const noexcept {
|
||||
return 0;
|
||||
}
|
||||
std::string NativeFile::GetDescription() const noexcept {
|
||||
if (!handle_) {
|
||||
return "unix file descriptor: "+path_.string();
|
||||
} else {
|
||||
return "unix file descriptor (active): "+path_.string();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nf7
|
||||
|
@ -1,77 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/future.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
template <typename T>
|
||||
class Task : public nf7::Context,
|
||||
public std::enable_shared_from_this<Task<T>> {
|
||||
public:
|
||||
class Holder;
|
||||
|
||||
using nf7::Context::Context;
|
||||
Task(const Task&) = delete;
|
||||
Task(Task&&) = delete;
|
||||
Task& operator=(const Task&) = delete;
|
||||
Task& operator=(Task&&) = delete;
|
||||
|
||||
void Start() noexcept {
|
||||
coro_ = Proc();
|
||||
fu_ = coro_->Start(self());
|
||||
}
|
||||
void Abort() noexcept {
|
||||
coro_->Abort();
|
||||
}
|
||||
|
||||
auto self() noexcept {
|
||||
return std::enable_shared_from_this<Task<T>>::shared_from_this();
|
||||
}
|
||||
auto fu() noexcept { return *fu_; }
|
||||
|
||||
protected:
|
||||
virtual typename nf7::Future<T>::Coro Proc() noexcept = 0;
|
||||
|
||||
private:
|
||||
std::optional<typename nf7::Future<T>::Coro> coro_;
|
||||
std::optional<nf7::Future<T>> fu_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Task<T>::Holder final {
|
||||
public:
|
||||
Holder() = default;
|
||||
Holder(const std::shared_ptr<Task<T>>& ctx) noexcept : ctx_(ctx) {
|
||||
}
|
||||
~Holder() noexcept {
|
||||
Abort();
|
||||
}
|
||||
Holder(const Holder&) = delete;
|
||||
Holder(Holder&& src) noexcept = default;
|
||||
Holder& operator=(const Holder&) = delete;
|
||||
Holder& operator=(Holder&& src) noexcept {
|
||||
if (this != &src) {
|
||||
Abort();
|
||||
ctx_ = std::move(src.ctx_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Abort() noexcept {
|
||||
if (auto ctx = ctx_.lock()) ctx->Abort();
|
||||
ctx_ = {};
|
||||
}
|
||||
|
||||
std::shared_ptr<Task<T>> lock() const noexcept { return ctx_.lock(); }
|
||||
|
||||
private:
|
||||
std::weak_ptr<Task<T>> ctx_;
|
||||
};
|
||||
|
||||
} // namespace nf7
|
@ -5,6 +5,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
@ -120,6 +121,20 @@ class Value {
|
||||
const ConstTuple tuple() const { return get<Tuple>(); }
|
||||
const DataPtr& data() const { return get<DataPtr>(); }
|
||||
|
||||
template <typename I>
|
||||
I integer() const {
|
||||
const auto ret = integer();
|
||||
if constexpr (std::is_unsigned<I>::value) {
|
||||
if (ret < 0) {
|
||||
throw IncompatibleException("integer underflow");
|
||||
}
|
||||
} else {
|
||||
if (ret != static_cast<Integer>(static_cast<I>(ret))) {
|
||||
throw IncompatibleException("integer out of range");
|
||||
}
|
||||
}
|
||||
return static_cast<I>(ret);
|
||||
}
|
||||
const Value& tuple(size_t idx) const {
|
||||
auto& tup = *tuple();
|
||||
return idx < tup.size()? tup[idx].second:
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
@ -12,161 +11,265 @@
|
||||
#include <imgui.h>
|
||||
#include <imgui_stdlib.h>
|
||||
#include <yas/serialize.hpp>
|
||||
#include <yas/types/std/chrono.hpp>
|
||||
#include <yas/types/std/string.hpp>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/async_buffer.hh"
|
||||
#include "common/async_buffer_adaptor.hh"
|
||||
#include "common/dir_item.hh"
|
||||
#include "common/generic_context.hh"
|
||||
#include "common/generic_memento.hh"
|
||||
#include "common/generic_type_info.hh"
|
||||
#include "common/gui_popup.hh"
|
||||
#include "common/gui_window.hh"
|
||||
#include "common/life.hh"
|
||||
#include "common/native_file.hh"
|
||||
#include "common/node.hh"
|
||||
#include "common/ptr_selector.hh"
|
||||
#include "common/queue.hh"
|
||||
#include "common/thread.hh"
|
||||
#include "common/yas_std_filesystem.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
namespace {
|
||||
|
||||
class NativeFile final : public File,
|
||||
public nf7::DirItem {
|
||||
class NativeFile final : public nf7::File,
|
||||
public nf7::DirItem, public nf7::Node {
|
||||
public:
|
||||
static inline const GenericTypeInfo<NativeFile> kType = {
|
||||
"System/NativeFile", {"nf7::AsyncBuffer", "nf7::DirItem"}};
|
||||
static inline const nf7::GenericTypeInfo<NativeFile> kType = {
|
||||
"System/NativeFile", {"nf7::DirItem", "nf7::Node"}};
|
||||
static void UpdateTypeTooltip() noexcept {
|
||||
ImGui::TextUnformatted("Reads/Writes a file placed on native filesystem.");
|
||||
ImGui::Bullet(); ImGui::TextUnformatted("implements nf7::AsyncBuffer");
|
||||
ImGui::Bullet(); ImGui::TextUnformatted("basepath is environment native path");
|
||||
ImGui::TextUnformatted("Read/Write a file placed on native filesystem.");
|
||||
ImGui::Bullet(); ImGui::TextUnformatted("implements nf7::Node");
|
||||
}
|
||||
|
||||
NativeFile(Env& env, const std::filesystem::path& path = "", std::string_view mode = "") noexcept :
|
||||
class Lambda;
|
||||
|
||||
struct SharedData final {
|
||||
std::optional<nf7::NativeFile> nfile;
|
||||
std::atomic<bool> refresh;
|
||||
};
|
||||
struct Runner final {
|
||||
struct Task {
|
||||
std::shared_ptr<nf7::Node::Lambda> callee;
|
||||
std::shared_ptr<nf7::Node::Lambda> caller;
|
||||
std::function<nf7::Value(const std::shared_ptr<SharedData>&)> func;
|
||||
|
||||
std::filesystem::path npath;
|
||||
nf7::NativeFile::Flags flags;
|
||||
};
|
||||
|
||||
Runner(const std::shared_ptr<SharedData>& shared) noexcept :
|
||||
shared_(shared) {
|
||||
}
|
||||
void operator()(Task&&) noexcept;
|
||||
|
||||
private:
|
||||
std::shared_ptr<SharedData> shared_;
|
||||
};
|
||||
using Thread = nf7::Thread<Runner, Runner::Task>;
|
||||
|
||||
struct Data final {
|
||||
std::filesystem::path npath;
|
||||
std::string mode;
|
||||
};
|
||||
|
||||
NativeFile(nf7::Env& env, Data&& data = {}) noexcept :
|
||||
nf7::File(kType, env),
|
||||
nf7::DirItem(
|
||||
nf7::DirItem::kTooltip |
|
||||
nf7::DirItem::kWidget),
|
||||
npath_(path), mode_(mode) {
|
||||
nf7::DirItem(nf7::DirItem::kWidget),
|
||||
life_(*this),
|
||||
shared_(std::make_shared<SharedData>()),
|
||||
th_(std::make_shared<Thread>(*this, Runner {shared_})),
|
||||
mem_(std::move(data)),
|
||||
config_popup_(*this) {
|
||||
mem_.onRestore = [this]() { Touch(); };
|
||||
mem_.onCommit = [this]() { Touch(); };
|
||||
}
|
||||
|
||||
NativeFile(Env& env, Deserializer& ar) : NativeFile(env) {
|
||||
ar(npath_, mode_, lastmod_);
|
||||
NativeFile(nf7::Env& env, nf7::Deserializer&) : NativeFile(env) {
|
||||
// TODO
|
||||
}
|
||||
void Serialize(Serializer& ar) const noexcept override {
|
||||
ar(npath_, mode_, lastmod_);
|
||||
void Serialize(nf7::Serializer&) const noexcept override {
|
||||
}
|
||||
std::unique_ptr<File> Clone(Env& env) const noexcept override {
|
||||
return std::make_unique<NativeFile>(env, npath_, mode_);
|
||||
std::unique_ptr<nf7::File> Clone(Env& env) const noexcept override {
|
||||
return std::make_unique<NativeFile>(env, Data {data()});
|
||||
}
|
||||
|
||||
std::shared_ptr<nf7::Node::Lambda> CreateLambda(
|
||||
const std::shared_ptr<nf7::Node::Lambda>&) noexcept override;
|
||||
|
||||
std::span<const std::string> GetInputs() const noexcept override {
|
||||
static const std::vector<std::string> kInputs = {"command"};
|
||||
return kInputs;
|
||||
}
|
||||
std::span<const std::string> GetOutputs() const noexcept override {
|
||||
static const std::vector<std::string> kOutputs = {"result"};
|
||||
return kOutputs;
|
||||
}
|
||||
|
||||
void Update() noexcept override;
|
||||
void UpdateTooltip() noexcept override;
|
||||
void UpdateWidget() noexcept override;
|
||||
|
||||
void Handle(const Event& ev) noexcept override {
|
||||
switch (ev.type) {
|
||||
case Event::kAdd:
|
||||
Reset();
|
||||
return;
|
||||
case Event::kRemove:
|
||||
buf_ = nullptr;
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
File::Interface* interface(const std::type_info& t) noexcept override {
|
||||
return InterfaceSelector<nf7::AsyncBuffer, nf7::DirItem>(t).Select(this, buf_.get());
|
||||
return InterfaceSelector<nf7::DirItem, nf7::Node>(t).Select(this);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<nf7::AsyncBufferAdaptor> buf_;
|
||||
nf7::Life<NativeFile> life_;
|
||||
|
||||
std::shared_ptr<SharedData> shared_;
|
||||
std::shared_ptr<Thread> th_;
|
||||
|
||||
// persistent params
|
||||
std::filesystem::path npath_;
|
||||
std::string mode_;
|
||||
std::filesystem::file_time_type lastmod_;
|
||||
|
||||
nf7::GenericMemento<Data> mem_;
|
||||
|
||||
void Reset() noexcept {
|
||||
bool exlock = false;
|
||||
nf7::Buffer::Flags flags = 0;
|
||||
for (auto c : mode_) {
|
||||
if (c == 'x') exlock = true;
|
||||
flags |=
|
||||
c == 'r'? nf7::Buffer::kRead:
|
||||
c == 'w'? nf7::Buffer::kWrite: 0;
|
||||
const Data& data() const noexcept { return mem_.data(); }
|
||||
Data& data() noexcept { return mem_.data(); }
|
||||
|
||||
|
||||
// GUI popup
|
||||
struct ConfigPopup final : private nf7::gui::Popup {
|
||||
public:
|
||||
ConfigPopup(NativeFile& f) noexcept :
|
||||
nf7::gui::Popup("ConfigPopup"), f_(&f) {
|
||||
}
|
||||
auto buf = std::make_shared<
|
||||
nf7::NativeFile>(*this, env().npath()/npath_, flags, exlock);
|
||||
buf_ = std::make_shared<nf7::AsyncBufferAdaptor>(buf, buf);
|
||||
|
||||
void Open() noexcept {
|
||||
npath_ = f_->data().npath.generic_string();
|
||||
nf7::gui::Popup::Open();
|
||||
}
|
||||
void Update() noexcept;
|
||||
|
||||
private:
|
||||
NativeFile* const f_;
|
||||
|
||||
std::string npath_;
|
||||
bool read_, write_;
|
||||
} config_popup_;
|
||||
};
|
||||
|
||||
class NativeFile::Lambda final : public nf7::Node::Lambda,
|
||||
public std::enable_shared_from_this<NativeFile::Lambda> {
|
||||
public:
|
||||
Lambda(NativeFile& f, const std::shared_ptr<nf7::Node::Lambda>& parent) noexcept :
|
||||
nf7::Node::Lambda(f, parent), f_(f.life_) {
|
||||
}
|
||||
|
||||
void Handle(std::string_view, const nf7::Value& v,
|
||||
const std::shared_ptr<nf7::Node::Lambda>& caller) noexcept override
|
||||
try {
|
||||
f_.EnforceAlive();
|
||||
|
||||
const auto type = v.tuple("type").string();
|
||||
if (type == "read") {
|
||||
const auto offset = v.tuple("offset").integer<size_t>();
|
||||
const auto size = v.tuple("size").integer<size_t>();
|
||||
Push(caller, [offset, size](auto& shared) {
|
||||
std::vector<uint8_t> buf;
|
||||
buf.resize(size);
|
||||
const auto actual = shared->nfile->Read(offset, buf.data(), size);
|
||||
buf.resize(actual);
|
||||
return nf7::Value {std::move(buf)};
|
||||
});
|
||||
} else if (type == "write") {
|
||||
const auto offset = v.tuple("offset").integer<size_t>();
|
||||
const auto buf = v.tuple("buf").vector();
|
||||
Push(caller, [offset, buf](auto& shared) {
|
||||
const auto ret = shared->nfile->Write(offset, buf->data(), buf->size());
|
||||
return nf7::Value {static_cast<nf7::Value::Integer>(ret)};
|
||||
});
|
||||
} else if (type == "truncate") {
|
||||
const auto size = v.tuple("size").integer<size_t>();
|
||||
Push(caller, [size](auto& shared) {
|
||||
shared->nfile->Truncate(size);
|
||||
return nf7::Value::Pulse {};
|
||||
});
|
||||
} else {
|
||||
throw nf7::Exception {"unknown command type: "+type};
|
||||
}
|
||||
} catch (nf7::Exception&) {
|
||||
// TODO log
|
||||
}
|
||||
|
||||
private:
|
||||
nf7::Life<NativeFile>::Ref f_;
|
||||
|
||||
void Push(const std::shared_ptr<nf7::Node::Lambda>& caller, auto&& f) noexcept {
|
||||
const auto& mode = f_->data().mode;
|
||||
nf7::NativeFile::Flags flags = 0;
|
||||
if (std::string::npos != mode.find('r')) flags |= nf7::NativeFile::kRead;
|
||||
if (std::string::npos != mode.find('w')) flags |= nf7::NativeFile::kWrite;
|
||||
|
||||
auto self = shared_from_this();
|
||||
f_->th_->Push(self, NativeFile::Runner::Task {
|
||||
.callee = self,
|
||||
.caller = caller,
|
||||
.func = std::move(f),
|
||||
.npath = f_->data().npath,
|
||||
.flags = flags,
|
||||
});
|
||||
}
|
||||
};
|
||||
std::shared_ptr<nf7::Node::Lambda> NativeFile::CreateLambda(
|
||||
const std::shared_ptr<nf7::Node::Lambda>& parent) noexcept {
|
||||
return std::make_shared<NativeFile::Lambda>(*this, parent);
|
||||
}
|
||||
void NativeFile::Runner::operator()(Task&& t) noexcept
|
||||
try {
|
||||
auto callee = t.callee;
|
||||
auto caller = t.caller;
|
||||
|
||||
if (shared_->refresh.exchange(false) || !shared_->nfile) {
|
||||
shared_->nfile.emplace(callee->env(), callee->initiator(), t.npath, t.flags);
|
||||
}
|
||||
auto ret = t.func(shared_);
|
||||
callee->env().ExecSub(callee, [callee, caller, ret = std::move(ret)]() {
|
||||
caller->Handle("result", ret, callee);
|
||||
});
|
||||
} catch (nf7::Exception&) {
|
||||
// TODO log
|
||||
}
|
||||
|
||||
|
||||
void NativeFile::Update() noexcept {
|
||||
// file update check
|
||||
try {
|
||||
const auto lastmod = std::filesystem::last_write_time(env().npath()/npath_);
|
||||
const auto npath = env().npath() / data().npath;
|
||||
const auto lastmod = std::filesystem::last_write_time(npath);
|
||||
if (std::exchange(lastmod_, lastmod) < lastmod) {
|
||||
Touch();
|
||||
}
|
||||
} catch (std::filesystem::filesystem_error&) {
|
||||
}
|
||||
}
|
||||
void NativeFile::UpdateTooltip() noexcept {
|
||||
ImGui::Text("basepath: %s", env().npath().generic_string().c_str());
|
||||
ImGui::Text("path : %s", npath_.generic_string().c_str());
|
||||
ImGui::Text("mode : %s", mode_.c_str());
|
||||
}
|
||||
void NativeFile::UpdateWidget() noexcept {
|
||||
ImGui::TextUnformatted("System/NativeFile");
|
||||
|
||||
if (ImGui::Button("change referencee")) {
|
||||
ImGui::OpenPopup("ReplaceReferenceePopup");
|
||||
if (ImGui::Button("config")) {
|
||||
config_popup_.Open();
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup("ReplaceReferenceePopup")) {
|
||||
static std::string path;
|
||||
static bool flag_exlock;
|
||||
static bool flag_readable;
|
||||
static bool flag_writeable;
|
||||
|
||||
ImGui::TextUnformatted("System/NativeFile: config");
|
||||
if (ImGui::IsWindowAppearing()) {
|
||||
path = npath_.generic_string();
|
||||
flag_exlock = mode_.find('x') != std::string::npos;
|
||||
flag_readable = mode_.find('r') != std::string::npos;
|
||||
flag_writeable = mode_.find('w') != std::string::npos;
|
||||
}
|
||||
|
||||
ImGui::InputText("path", &path);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(
|
||||
"path to the native file system (base: '%s')",
|
||||
env().npath().generic_string().c_str());
|
||||
}
|
||||
ImGui::Checkbox("exclusive lock", &flag_exlock);
|
||||
ImGui::Checkbox("readable", &flag_readable);
|
||||
ImGui::Checkbox("writeable", &flag_writeable);
|
||||
config_popup_.Update();
|
||||
}
|
||||
void NativeFile::ConfigPopup::Update() noexcept {
|
||||
if (nf7::gui::Popup::Begin()) {
|
||||
ImGui::InputText("path", &npath_);
|
||||
ImGui::Checkbox("read", &read_);
|
||||
ImGui::Checkbox("write", &write_);
|
||||
|
||||
if (ImGui::Button("ok")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
||||
npath_ = path;
|
||||
mode_ = "";
|
||||
if (flag_exlock) mode_ += 'x';
|
||||
if (flag_readable) mode_ += 'r';
|
||||
if (flag_writeable) mode_ += 'w';
|
||||
auto& d = f_->data();
|
||||
d.npath = npath_;
|
||||
|
||||
auto ctx = std::make_shared<nf7::GenericContext>(*this, "resetting native file handle");
|
||||
env().ExecMain(ctx, [this]() { Reset(); Touch(); });
|
||||
d.mode = "";
|
||||
if (read_) d.mode += "r";
|
||||
if (write_) d.mode += "w";
|
||||
|
||||
f_->mem_.Commit();
|
||||
}
|
||||
|
||||
if (!std::filesystem::exists(env().npath()/path)) {
|
||||
ImGui::Bullet(); ImGui::TextUnformatted("target file seems to be missing...");
|
||||
if (!std::filesystem::exists(f_->env().npath()/npath_)) {
|
||||
ImGui::Bullet();
|
||||
ImGui::TextUnformatted("file not found");
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user