add System/NativeFile
This commit is contained in:
parent
50b84e9b65
commit
71162b98c1
@ -45,6 +45,7 @@ target_sources(nf7
|
||||
nf7.hh
|
||||
|
||||
common/aggregate_command.hh
|
||||
common/buffer.hh
|
||||
common/dir.hh
|
||||
common/dir_item.hh
|
||||
common/file_ref.hh
|
||||
@ -58,6 +59,7 @@ target_sources(nf7
|
||||
common/history.hh
|
||||
common/lambda.hh
|
||||
common/memento.hh
|
||||
common/native_file.hh
|
||||
common/node.hh
|
||||
common/node_link_store.hh
|
||||
common/logger.hh
|
||||
@ -68,11 +70,14 @@ target_sources(nf7
|
||||
common/wait_queue.hh
|
||||
common/yas.hh
|
||||
|
||||
$<$<PLATFORM_ID:Linux>:common/native_file_unix.cc>
|
||||
|
||||
file/luajit_context.cc
|
||||
file/node_network.cc
|
||||
file/system_dir.cc
|
||||
file/system_imgui_config.cc
|
||||
file/system_logger.cc
|
||||
file/system_native_file.cc
|
||||
)
|
||||
target_link_libraries(nf7
|
||||
PRIVATE
|
||||
|
91
common/buffer.hh
Normal file
91
common/buffer.hh
Normal file
@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
|
||||
#include <yas/serialize.hpp>
|
||||
#include <yas/types/std/string.hpp>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class Buffer : public nf7::File::Interface {
|
||||
public:
|
||||
enum Flag : uint8_t {
|
||||
kRead = 1 << 0,
|
||||
kWrite = 1 << 1,
|
||||
};
|
||||
using Flags = uint8_t;
|
||||
|
||||
// ReadLock and WriteLock are still alive even after Buffer is deleted
|
||||
class ReadLock;
|
||||
class WriteLock;
|
||||
|
||||
class LockException;
|
||||
class IOException;
|
||||
|
||||
Buffer() = default;
|
||||
Buffer(const Buffer&) = delete;
|
||||
Buffer(Buffer&&) = delete;
|
||||
Buffer& operator=(const Buffer&) = delete;
|
||||
Buffer& operator=(Buffer&&) = delete;
|
||||
|
||||
virtual inline std::future<std::unique_ptr<ReadLock>> LockForRead() noexcept;
|
||||
virtual inline std::future<std::unique_ptr<WriteLock>> LockForWrite() noexcept;
|
||||
|
||||
virtual size_t size() const noexcept = 0;
|
||||
virtual Flags flags() const noexcept = 0;
|
||||
};
|
||||
|
||||
class Buffer::ReadLock {
|
||||
public:
|
||||
ReadLock() = default;
|
||||
virtual ~ReadLock() = default;
|
||||
ReadLock(const ReadLock&) = delete;
|
||||
ReadLock(ReadLock&&) = delete;
|
||||
ReadLock& operator=(const ReadLock&) = delete;
|
||||
ReadLock& operator=(ReadLock&&) = delete;
|
||||
|
||||
virtual std::future<size_t> Read(
|
||||
size_t offset, uint8_t* buf, size_t size) noexcept = 0;
|
||||
};
|
||||
class Buffer::WriteLock {
|
||||
public:
|
||||
WriteLock() = default;
|
||||
virtual ~WriteLock() = default;
|
||||
WriteLock(const WriteLock&) = delete;
|
||||
WriteLock(WriteLock&&) = delete;
|
||||
WriteLock& operator=(const WriteLock&) = delete;
|
||||
WriteLock& operator=(WriteLock&&) = delete;
|
||||
|
||||
virtual std::future<size_t> Write(
|
||||
size_t offset, const uint8_t* buf, size_t size) noexcept = 0;
|
||||
|
||||
virtual std::future<size_t> Truncate(size_t) noexcept = 0;
|
||||
};
|
||||
|
||||
class Buffer::LockException : public nf7::Exception {
|
||||
public:
|
||||
using Exception::Exception;
|
||||
};
|
||||
class Buffer::IOException : public nf7::Exception {
|
||||
public:
|
||||
using Exception::Exception;
|
||||
};
|
||||
|
||||
std::future<std::unique_ptr<Buffer::ReadLock>> Buffer::LockForRead() noexcept {
|
||||
std::promise<std::unique_ptr<Buffer::ReadLock>> pro;
|
||||
pro.set_exception(std::make_exception_ptr<LockException>({"Buffer is not readable"}));
|
||||
return pro.get_future();
|
||||
}
|
||||
std::future<std::unique_ptr<Buffer::WriteLock>> Buffer::LockForWrite() noexcept {
|
||||
std::promise<std::unique_ptr<Buffer::WriteLock>> pro;
|
||||
pro.set_exception(std::make_exception_ptr<LockException>({"Buffer is not writeable"}));
|
||||
return pro.get_future();
|
||||
}
|
||||
|
||||
} // namespace nf7
|
50
common/native_file.hh
Normal file
50
common/native_file.hh
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class NativeFile final : public nf7::Context {
|
||||
public:
|
||||
enum Flag : uint8_t {
|
||||
kRead = 1 << 0,
|
||||
kWrite = 1 << 1,
|
||||
kExLock = 1 << 2,
|
||||
};
|
||||
using Flags = uint8_t;
|
||||
|
||||
NativeFile() = delete;
|
||||
NativeFile(Env& env, File::Id id, const std::filesystem::path& path, Flags flags) noexcept :
|
||||
Context(env, id), path_(path), flags_(flags) {
|
||||
}
|
||||
NativeFile(const NativeFile&) = delete;
|
||||
NativeFile(NativeFile&&) = delete;
|
||||
NativeFile& operator=(const NativeFile&) = delete;
|
||||
NativeFile& operator=(NativeFile&&) = delete;
|
||||
|
||||
void CleanUp() noexcept override;
|
||||
void Abort() noexcept override;
|
||||
|
||||
void Lock();
|
||||
void Unlock();
|
||||
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 GetMemoryUsage() const noexcept override;
|
||||
std::string GetDescription() const noexcept override;
|
||||
|
||||
private:
|
||||
const std::filesystem::path path_;
|
||||
const Flags flags_;
|
||||
|
||||
uint64_t handle_;
|
||||
};
|
||||
|
||||
} // namespace nf7
|
40
common/native_file_unix.cc
Normal file
40
common/native_file_unix.cc
Normal file
@ -0,0 +1,40 @@
|
||||
#include "common/native_file.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
void NativeFile::CleanUp() noexcept {
|
||||
}
|
||||
void NativeFile::Abort() noexcept {
|
||||
}
|
||||
|
||||
void NativeFile::Lock() {
|
||||
}
|
||||
void NativeFile::Unlock() {
|
||||
}
|
||||
|
||||
size_t NativeFile::Read(size_t offset, uint8_t* buf, size_t size) {
|
||||
(void) offset;
|
||||
(void) buf;
|
||||
(void) size;
|
||||
return 0;
|
||||
}
|
||||
size_t NativeFile::Write(size_t offset, const uint8_t* buf, size_t size) {
|
||||
(void) offset;
|
||||
(void) buf;
|
||||
(void) size;
|
||||
return 0;
|
||||
}
|
||||
size_t NativeFile::Truncate(size_t size) {
|
||||
(void) size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t NativeFile::GetMemoryUsage() const noexcept {
|
||||
return 0;
|
||||
}
|
||||
std::string NativeFile::GetDescription() const noexcept {
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace nf7
|
50
common/promise_queue.hh
Normal file
50
common/promise_queue.hh
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
|
||||
#include "common/queue.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
|
||||
class PromiseQueue final :
|
||||
public nf7::Queue<std::function<bool(void)>> {
|
||||
public:
|
||||
PromiseQueue() = default;
|
||||
|
||||
template <typename T>
|
||||
void Push(std::future<T>&& fu, auto&& f) {
|
||||
auto fu_ptr = std::make_shared<std::future<T>>(std::move(fu));
|
||||
auto task = [fu_ptr = std::move(fu_ptr), f = std::move(f)]() mutable {
|
||||
if (fu_ptr->wait_for(std::chrono::seconds(0)) != std::future_status::ready) {
|
||||
return false;
|
||||
}
|
||||
f(std::move(*fu_ptr));
|
||||
return true;
|
||||
};
|
||||
Queue<std::function<bool(void)>>::Push(std::move(task));
|
||||
}
|
||||
template <typename T>
|
||||
void Push(std::shared_future<T> fu, auto&& f) {
|
||||
auto task = [fu, f = std::move(f)]() mutable {
|
||||
if (fu.wait_for(std::chrono::seconds(0)) != std::future_status::ready) {
|
||||
return false;
|
||||
}
|
||||
f(std::move(fu));
|
||||
return true;
|
||||
};
|
||||
Queue<std::function<bool(void)>>::Push(std::move(task));
|
||||
}
|
||||
bool PopAndExec() noexcept {
|
||||
if (auto task = Pop()) {
|
||||
if ((*task)()) return true;
|
||||
Interrupt(std::move(*task));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace nf7
|
@ -13,15 +13,19 @@ template <typename T>
|
||||
class Queue {
|
||||
public:
|
||||
Queue() = default;
|
||||
Queue(const Queue&) = default;
|
||||
Queue(Queue&&) = default;
|
||||
Queue& operator=(const Queue&) = default;
|
||||
Queue& operator=(Queue&&) = default;
|
||||
Queue(const Queue&) = delete;
|
||||
Queue(Queue&&) = delete;
|
||||
Queue& operator=(const Queue&) = delete;
|
||||
Queue& operator=(Queue&&) = delete;
|
||||
|
||||
void Push(T&& task) noexcept {
|
||||
std::unique_lock<std::mutex> _(mtx_);
|
||||
tasks_.push_back(std::move(task));
|
||||
}
|
||||
void Interrupt(T&& task) noexcept {
|
||||
std::unique_lock<std::mutex> _(mtx_);
|
||||
tasks_.push_front(std::move(task));
|
||||
}
|
||||
std::optional<T> Pop() noexcept {
|
||||
std::unique_lock<std::mutex> k(mtx_);
|
||||
if (tasks_.empty()) return std::nullopt;
|
||||
@ -31,6 +35,11 @@ class Queue {
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Clear() noexcept {
|
||||
std::unique_lock<std::mutex> k(mtx_);
|
||||
tasks_.clear();
|
||||
}
|
||||
|
||||
protected:
|
||||
std::mutex mtx_;
|
||||
|
||||
|
33
common/yas_std_filesystem.hh
Normal file
33
common/yas_std_filesystem.hh
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
#include <yas/serialize.hpp>
|
||||
#include <yas/types/std/string.hpp>
|
||||
|
||||
|
||||
namespace yas::detail {
|
||||
|
||||
template <size_t F>
|
||||
struct serializer<
|
||||
type_prop::not_a_fundamental,
|
||||
ser_case::use_internal_serializer,
|
||||
F,
|
||||
std::filesystem::path> {
|
||||
public:
|
||||
template <typename Archive>
|
||||
static Archive& save(Archive& ar, const std::filesystem::path& p) {
|
||||
ar(p.generic_string());
|
||||
return ar;
|
||||
}
|
||||
template <typename Archive>
|
||||
static Archive& load(Archive& ar, std::filesystem::path& p) {
|
||||
std::string str;
|
||||
ar(str);
|
||||
p = std::filesystem::path(str).lexically_normal();
|
||||
return ar;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace yas::detail
|
419
file/system_native_file.cc
Normal file
419
file/system_native_file.cc
Normal file
@ -0,0 +1,419 @@
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <typeinfo>
|
||||
#include <utility>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <imgui_stdlib.h>
|
||||
#include <yas/serialize.hpp>
|
||||
#include <yas/types/std/string.hpp>
|
||||
|
||||
#include "nf7.hh"
|
||||
|
||||
#include "common/buffer.hh"
|
||||
#include "common/dir_item.hh"
|
||||
#include "common/generic_context.hh"
|
||||
#include "common/generic_type_info.hh"
|
||||
#include "common/gui_window.hh"
|
||||
#include "common/logger_pool.hh"
|
||||
#include "common/native_file.hh"
|
||||
#include "common/promise_queue.hh"
|
||||
#include "common/ptr_selector.hh"
|
||||
#include "common/queue.hh"
|
||||
#include "common/yas_std_filesystem.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
namespace {
|
||||
|
||||
class BufferAdaptor final : public nf7::Buffer {
|
||||
public:
|
||||
class Lock;
|
||||
|
||||
template <typename T>
|
||||
using PromisePtr = std::promise<std::unique_ptr<T>>;
|
||||
template <typename T>
|
||||
using SharedPromisePtr = std::shared_ptr<PromisePtr<T>>;
|
||||
|
||||
using LockTask = std::function<bool(void)>;
|
||||
using Task = std::function<void(void)>;
|
||||
|
||||
BufferAdaptor(nf7::Env& env) noexcept : env_(&env) {
|
||||
}
|
||||
|
||||
std::future<std::unique_ptr<nf7::Buffer::ReadLock>> LockForRead() noexcept override {
|
||||
auto pro = std::make_shared<PromisePtr<nf7::Buffer::ReadLock>>();
|
||||
LockForRead(pro);
|
||||
return pro->get_future();
|
||||
}
|
||||
void LockForRead(const SharedPromisePtr<nf7::Buffer::ReadLock>& pro) noexcept {
|
||||
if (!(flags_ & nf7::Buffer::kRead)) {
|
||||
pro->set_exception(std::make_exception_ptr<LockException>({"not readable"}));
|
||||
return;
|
||||
}
|
||||
|
||||
if (exclusive_ && lock_) {
|
||||
auto task = [self = self.lock(), pro]() mutable {
|
||||
if (self->exclusive_ && self->lock_) return false;
|
||||
self->LockForRead(pro);
|
||||
return true;
|
||||
};
|
||||
lockq_.Push(std::move(task));
|
||||
return;
|
||||
}
|
||||
CreateLock(pro);
|
||||
}
|
||||
|
||||
std::future<std::unique_ptr<nf7::Buffer::WriteLock>> LockForWrite() noexcept override {
|
||||
auto pro = std::make_shared<PromisePtr<nf7::Buffer::WriteLock>>();
|
||||
LockForWrite(pro);
|
||||
return pro->get_future();
|
||||
}
|
||||
void LockForWrite(const SharedPromisePtr<nf7::Buffer::WriteLock>& pro) noexcept {
|
||||
if (!(flags_ & nf7::Buffer::kWrite)) {
|
||||
pro->set_exception(std::make_exception_ptr<LockException>({"not writeable"}));
|
||||
return;
|
||||
}
|
||||
|
||||
if (lock_) {
|
||||
auto task = [self = self.lock(), pro]() {
|
||||
if (self->lock_) return false;
|
||||
self->LockForWrite(pro);
|
||||
return true;
|
||||
};
|
||||
lockq_.Push(std::move(task));
|
||||
return;
|
||||
}
|
||||
exclusive_ = true;
|
||||
CreateLock(pro);
|
||||
}
|
||||
|
||||
void Reset(const std::filesystem::path& path, std::string_view mode) noexcept {
|
||||
impl_ = nullptr;
|
||||
path_ = path;
|
||||
|
||||
flags_ = 0;
|
||||
for (auto c : mode) {
|
||||
flags_ |=
|
||||
c == 'r'? nf7::Buffer::kRead:
|
||||
c == 'w'? nf7::Buffer::kWrite: 0;
|
||||
impl_flags_ |=
|
||||
c == 'r'? nf7::NativeFile::kRead:
|
||||
c == 'w'? nf7::NativeFile::kWrite:
|
||||
c == 'x'? nf7::NativeFile::kExLock: 0;
|
||||
}
|
||||
}
|
||||
|
||||
const std::filesystem::path& path() const noexcept { return path_; }
|
||||
Flags flags() const noexcept override { return flags_; }
|
||||
size_t size() const noexcept override
|
||||
try {
|
||||
return std::filesystem::file_size(path_);
|
||||
} catch (std::filesystem::filesystem_error&) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
nf7::File::Id owner;
|
||||
std::weak_ptr<BufferAdaptor> self;
|
||||
|
||||
private:
|
||||
Env* const env_;
|
||||
|
||||
std::filesystem::path path_;
|
||||
Flags flags_ = 0;
|
||||
|
||||
size_t size_ = 0;
|
||||
|
||||
nf7::NativeFile::Flags impl_flags_;
|
||||
std::shared_ptr<nf7::NativeFile> impl_;
|
||||
|
||||
bool exclusive_ = false;
|
||||
size_t lock_ = 0;
|
||||
nf7::Queue<LockTask> lockq_; // pushed from only main thread
|
||||
|
||||
std::mutex mtx_;
|
||||
bool working_ = false;
|
||||
nf7::Queue<Task> taskq_;
|
||||
|
||||
void CreateImplIf() noexcept {
|
||||
if (!impl_) {
|
||||
impl_ = std::make_shared<nf7::NativeFile>(*env_, owner, path_, impl_flags_);
|
||||
}
|
||||
}
|
||||
void CreateLock(const auto& pro) noexcept;
|
||||
|
||||
void HandleNextLock() noexcept {
|
||||
assert(lock_ == 0);
|
||||
|
||||
auto task = [self = self.lock()]() {
|
||||
while (auto task = self->lockq_.Pop()) {
|
||||
if (!(*task)()) {
|
||||
self->lockq_.Interrupt(std::move(*task));
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
CreateImplIf();
|
||||
env_->ExecMain(impl_, std::move(task));
|
||||
}
|
||||
void Exec(Task&& task) noexcept {
|
||||
taskq_.Push(std::move(task));
|
||||
std::unique_lock<std::mutex> k(mtx_);
|
||||
if (!std::exchange(working_, true)) {
|
||||
CreateImplIf();
|
||||
env_->ExecAsync(impl_, [self = self.lock()]() { self->HandleTask(); });
|
||||
}
|
||||
}
|
||||
void HandleTask() noexcept {
|
||||
for (;;) {
|
||||
std::unique_lock<std::mutex> k(mtx_);
|
||||
auto task = taskq_.Pop();
|
||||
if (!task) {
|
||||
working_ = false;
|
||||
break;
|
||||
}
|
||||
k.unlock();
|
||||
|
||||
(*task)();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class BufferAdaptor::Lock : public nf7::Buffer::ReadLock, public nf7::Buffer::WriteLock {
|
||||
public:
|
||||
Lock(const std::shared_ptr<BufferAdaptor>& buf) noexcept :
|
||||
buf_(buf), impl_(buf_->impl_) {
|
||||
if (buf_->lock_++ == 0) {
|
||||
impl_->Lock();
|
||||
}
|
||||
}
|
||||
~Lock() noexcept {
|
||||
if (--buf_->lock_ == 0) {
|
||||
buf_->Exec([impl = impl_]() { impl->Unlock(); });
|
||||
buf_->exclusive_ = false;
|
||||
buf_->HandleNextLock();
|
||||
}
|
||||
}
|
||||
|
||||
std::future<size_t> Read(size_t offset, uint8_t* buf, size_t size) noexcept override {
|
||||
auto pro = std::make_shared<std::promise<size_t>>();
|
||||
auto task = [impl = impl_, pro, offset, buf, size]() {
|
||||
pro->set_value(impl->Read(offset, buf, size));
|
||||
};
|
||||
buf_->Exec(std::move(task));
|
||||
return pro->get_future();
|
||||
}
|
||||
std::future<size_t> Write(size_t offset, const uint8_t* buf, size_t size) noexcept override {
|
||||
auto pro = std::make_shared<std::promise<size_t>>();
|
||||
auto task = [impl = impl_, pro, offset, buf, size]() {
|
||||
pro->set_value(impl->Write(offset, buf, size));
|
||||
};
|
||||
buf_->Exec(std::move(task));
|
||||
return pro->get_future();
|
||||
}
|
||||
std::future<size_t> Truncate(size_t size) noexcept override {
|
||||
auto pro = std::make_shared<std::promise<size_t>>();
|
||||
auto task = [impl = impl_, pro, size]() {
|
||||
pro->set_value(impl->Truncate(size));
|
||||
};
|
||||
buf_->Exec(std::move(task));
|
||||
return pro->get_future();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<BufferAdaptor> buf_;
|
||||
std::shared_ptr<nf7::NativeFile> impl_;
|
||||
};
|
||||
|
||||
void BufferAdaptor::CreateLock(const auto& pro) noexcept {
|
||||
try {
|
||||
env_->GetFile(owner);
|
||||
} catch (ExpiredException&) {
|
||||
lockq_.Clear();
|
||||
pro->set_exception(std::make_exception_ptr<LockException>({"file expired"}));
|
||||
return;
|
||||
}
|
||||
CreateImplIf();
|
||||
|
||||
auto task = [self = self.lock(), pro]() {
|
||||
try {
|
||||
auto k = std::make_unique<Lock>(self);
|
||||
pro->set_value(std::move(k));
|
||||
} catch (...) {
|
||||
pro->set_exception(std::current_exception());
|
||||
}
|
||||
};
|
||||
Exec(std::move(task));
|
||||
}
|
||||
|
||||
|
||||
class NativeFile final : public File,
|
||||
public nf7::DirItem {
|
||||
public:
|
||||
static inline const GenericTypeInfo<NativeFile> kType = {
|
||||
"System/NativeFile", {"Buffer", "DirItem"}};
|
||||
|
||||
NativeFile(Env& env, const std::filesystem::path& path = "", std::string_view mode = "") noexcept :
|
||||
File(kType, env), DirItem(DirItem::kMenu | DirItem::kTooltip),
|
||||
logger_(*this), buf_(std::make_shared<BufferAdaptor>(env)),
|
||||
test_win_(*this, "NativeFile Tester"),
|
||||
npath_(path), mode_(mode) {
|
||||
buf_->self = buf_;
|
||||
Reset();
|
||||
}
|
||||
|
||||
NativeFile(Env& env, Deserializer& ar) : NativeFile(env) {
|
||||
ar(npath_, mode_);
|
||||
Reset();
|
||||
}
|
||||
void Serialize(Serializer& ar) const noexcept override {
|
||||
ar(npath_, mode_);
|
||||
}
|
||||
std::unique_ptr<File> Clone(Env& env) const noexcept override {
|
||||
return std::make_unique<NativeFile>(env, npath_, mode_);
|
||||
}
|
||||
|
||||
void Update() noexcept override;
|
||||
void UpdateMenu() noexcept override;
|
||||
void UpdateTooltip() noexcept override;
|
||||
|
||||
void Handle(const Event& ev) noexcept override {
|
||||
switch (ev.type) {
|
||||
case Event::kAdd:
|
||||
buf_->owner = id();
|
||||
return;
|
||||
case Event::kRemove:
|
||||
buf_->owner = 0;
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
File::Interface* interface(const std::type_info& t) noexcept override {
|
||||
return InterfaceSelector<nf7::Buffer, nf7::DirItem>(t).Select(this, buf_.get());
|
||||
}
|
||||
|
||||
private:
|
||||
nf7::LoggerPool logger_;
|
||||
std::shared_ptr<BufferAdaptor> buf_;
|
||||
|
||||
const char* popup_ = nullptr;
|
||||
|
||||
gui::Window test_win_;
|
||||
uint32_t test_offset_ = 0;
|
||||
uint32_t test_size_ = 1;
|
||||
std::string test_msg_;
|
||||
std::vector<uint8_t> test_buf_;
|
||||
nf7::PromiseQueue testq_;
|
||||
|
||||
// persistent params
|
||||
std::filesystem::path npath_;
|
||||
std::string mode_;
|
||||
|
||||
|
||||
void Reset() noexcept {
|
||||
buf_->Reset(env().npath() / npath_, mode_);
|
||||
}
|
||||
|
||||
void TestLock(std::future<std::unique_ptr<nf7::Buffer::ReadLock>>& fu) noexcept
|
||||
try {
|
||||
auto k = std::move(fu.get());
|
||||
test_msg_ = "OK: lock acquired";
|
||||
|
||||
test_buf_.resize(test_size_);
|
||||
testq_.Push(k->Read(test_offset_, test_buf_.data(), test_size_),
|
||||
[this](auto&& f) { TestRead(f); });
|
||||
} catch (Exception& e) {
|
||||
test_msg_ = "NG:"+e.msg();
|
||||
}
|
||||
void TestRead(std::future<size_t>& fu) noexcept
|
||||
try {
|
||||
const auto n = std::move(fu.get());
|
||||
test_msg_ = "DONE: read "+std::to_string(n)+" bytes";
|
||||
} catch (Exception& e) {
|
||||
test_msg_ = "NG:"+e.msg();
|
||||
}
|
||||
};
|
||||
|
||||
void NativeFile::Update() noexcept {
|
||||
while (testq_.PopAndExec());
|
||||
|
||||
if (const auto popup = std::exchange(popup_, nullptr)) {
|
||||
ImGui::OpenPopup(popup);
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup("ConfigPopup")) {
|
||||
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);
|
||||
|
||||
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 ctx = std::make_shared<nf7::GenericContext>(env(), id());
|
||||
ctx->description() = "resetting native file handle";
|
||||
env().ExecMain(ctx, [this]() { Reset(); });
|
||||
}
|
||||
|
||||
if (!std::filesystem::exists(env().npath()/path)) {
|
||||
ImGui::Bullet(); ImGui::TextUnformatted("target file seems to be missing...");
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if (test_win_.Begin()) {
|
||||
static const uint32_t kMax = 1024*1024, kZero = 0, kOne = 1;
|
||||
ImGui::DragScalar("offset", ImGuiDataType_U32, &test_offset_, 1, &kZero, &kMax);
|
||||
ImGui::DragScalar("size", ImGuiDataType_U32, &test_size_, 1, &kOne, &kMax);
|
||||
if (ImGui::Button("exec")) {
|
||||
auto fu = buf_->LockForRead();
|
||||
testq_.Push(std::move(fu), [this](auto&& fu) { TestLock(fu); });
|
||||
}
|
||||
ImGui::TextUnformatted(test_msg_.c_str());
|
||||
}
|
||||
test_win_.End();
|
||||
}
|
||||
void NativeFile::UpdateMenu() noexcept {
|
||||
if (ImGui::MenuItem("config")) {
|
||||
popup_ = "ConfigPopup";
|
||||
}
|
||||
ImGui::MenuItem("test", nullptr, &test_win_.shown());
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nf7
|
Loading…
Reference in New Issue
Block a user