Compare commits
No commits in common. "v0.2.0" and "main" have entirely different histories.
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1 @@
|
|||||||
/build/
|
/build*/
|
||||||
|
200
CMakeLists.txt
200
CMakeLists.txt
@ -1,168 +1,54 @@
|
|||||||
cmake_minimum_required(VERSION 3.18)
|
cmake_minimum_required(VERSION 3.27)
|
||||||
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
|
|
||||||
|
|
||||||
# ---- configuration ----
|
project(nf7 C)
|
||||||
project(nf7 C CXX)
|
|
||||||
|
|
||||||
option(NF7_STATIC "link all libs statically" ON)
|
set(CMAKE_C_STANDARD 23)
|
||||||
option(NF7_SANITIZE_THREAD "use thread sanitizer" OFF)
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
set(NF7_OPTIONS_WARNING
|
include(tool/meta.cmake)
|
||||||
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
|
|
||||||
-Wall -Werror -pedantic-errors -Wextra -Wconversion -Wsign-conversion>
|
|
||||||
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>>:
|
|
||||||
-Wno-overloaded-virtual>
|
|
||||||
$<$<CXX_COMPILER_ID:MSVC>:
|
|
||||||
/W4 /WX /Zc:__cplusplus /external:anglebrackets /external:W0>
|
|
||||||
)
|
|
||||||
if (NF7_SANITIZE_THREAD)
|
|
||||||
set(NF7_OPTIONS_SANITIZE
|
|
||||||
$<$<CONFIG:Debug>:$<$<CXX_COMPILER_ID:GNU>:
|
|
||||||
-fsanitize=thread -fno-omit-frame-pointer>>
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
set(NF7_OPTIONS_SANITIZE
|
|
||||||
$<$<CONFIG:Debug>:$<$<CXX_COMPILER_ID:GNU>:
|
|
||||||
-fsanitize=address -fsanitize=undefined -fsanitize=leak -fno-omit-frame-pointer>>
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(NF7_GENERATED_INCLUDE_DIR "${PROJECT_BINARY_DIR}/include/generated")
|
|
||||||
file(MAKE_DIRECTORY "${NF7_GENERATED_INCLUDE_DIR}")
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
|
|
||||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
|
||||||
|
|
||||||
|
# ---- thirdparty import
|
||||||
add_subdirectory(thirdparty EXCLUDE_FROM_ALL)
|
add_subdirectory(thirdparty EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
|
# ---- common config
|
||||||
|
add_library(nf7config INTERFACE)
|
||||||
|
target_include_directories(nf7config
|
||||||
|
INTERFACE
|
||||||
|
${PROJECT_SOURCE_DIR}
|
||||||
|
${NF7_GENERATED_DIR}
|
||||||
|
)
|
||||||
|
target_compile_options(nf7config INTERFACE
|
||||||
|
$<$<CXX_COMPILER_ID:MSVC>:
|
||||||
|
/W4
|
||||||
|
$<$<CONFIG:Debug>:/WX>
|
||||||
|
>
|
||||||
|
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:
|
||||||
|
-Wall -Wextra -Wpedantic
|
||||||
|
-Wno-gnu-zero-variadic-macro-arguments
|
||||||
|
$<$<CONFIG:Debug>:-Werror>
|
||||||
|
>
|
||||||
|
)
|
||||||
|
|
||||||
# ---- application ----
|
# ---- test library
|
||||||
|
add_subdirectory(test EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
|
# ---- util library
|
||||||
|
add_subdirectory(util)
|
||||||
|
|
||||||
|
# ---- interface library
|
||||||
|
add_library(nf7if INTERFACE)
|
||||||
|
target_sources(nf7if INTERFACE nf7.h)
|
||||||
|
target_link_libraries(nf7if INTERFACE nf7config nf7util uv)
|
||||||
|
|
||||||
|
# ---- core library
|
||||||
|
add_subdirectory(core)
|
||||||
|
|
||||||
|
# ---- main executable
|
||||||
add_executable(nf7)
|
add_executable(nf7)
|
||||||
target_include_directories(nf7 PRIVATE . "${PROJECT_BINARY_DIR}/include")
|
target_sources(nf7 PRIVATE main.c)
|
||||||
target_compile_options(nf7 PRIVATE
|
|
||||||
${NF7_OPTIONS_WARNING}
|
|
||||||
${NF7_OPTIONS_SANITIZE}
|
|
||||||
)
|
|
||||||
target_link_options(nf7 PRIVATE
|
|
||||||
${NF7_OPTIONS_SANITIZE}
|
|
||||||
)
|
|
||||||
target_compile_definitions(nf7
|
|
||||||
PRIVATE
|
|
||||||
IMGUI_DEFINE_MATH_OPERATORS
|
|
||||||
|
|
||||||
$<$<PLATFORM_ID:Darwin>:GL_SILENCE_DEPRECATION>
|
|
||||||
$<$<PLATFORM_ID:Darwin>:_GNU_SOURCE>
|
|
||||||
)
|
|
||||||
target_sources(nf7
|
|
||||||
PRIVATE
|
|
||||||
init.hh
|
|
||||||
main.cc
|
|
||||||
nf7.cc
|
|
||||||
nf7.hh
|
|
||||||
theme.hh
|
|
||||||
|
|
||||||
common/aggregate_command.hh
|
|
||||||
common/audio_queue.hh
|
|
||||||
common/dir.hh
|
|
||||||
common/dir_item.hh
|
|
||||||
common/file_base.hh
|
|
||||||
common/file_holder.hh
|
|
||||||
common/file_holder.cc
|
|
||||||
common/future.hh
|
|
||||||
common/generic_context.hh
|
|
||||||
common/generic_history.hh
|
|
||||||
common/generic_memento.hh
|
|
||||||
common/generic_type_info.hh
|
|
||||||
common/generic_watcher.hh
|
|
||||||
common/gui_dnd.hh
|
|
||||||
common/gui_config.hh
|
|
||||||
common/gui_context.hh
|
|
||||||
common/gui_file.hh
|
|
||||||
common/gui_file.cc
|
|
||||||
common/gui_node.hh
|
|
||||||
common/gui_popup.hh
|
|
||||||
common/gui_popup.cc
|
|
||||||
common/gui_timeline.hh
|
|
||||||
common/gui_timeline.cc
|
|
||||||
common/gui_value.hh
|
|
||||||
common/gui_value.cc
|
|
||||||
common/gui_window.hh
|
|
||||||
common/history.hh
|
|
||||||
common/life.hh
|
|
||||||
common/logger.hh
|
|
||||||
common/logger_ref.hh
|
|
||||||
common/luajit.hh
|
|
||||||
common/luajit.cc
|
|
||||||
common/luajit_queue.hh
|
|
||||||
common/luajit_ref.hh
|
|
||||||
common/luajit_thread.hh
|
|
||||||
common/luajit_thread.cc
|
|
||||||
common/memento.hh
|
|
||||||
common/memento_recorder.hh
|
|
||||||
common/mutable_memento.hh
|
|
||||||
common/mutex.hh
|
|
||||||
common/nfile.hh
|
|
||||||
common/nfile_watcher.hh
|
|
||||||
common/node.hh
|
|
||||||
common/node_link_store.hh
|
|
||||||
common/node_root_lambda.hh
|
|
||||||
common/node_root_select_lambda.hh
|
|
||||||
common/ptr_selector.hh
|
|
||||||
common/queue.hh
|
|
||||||
common/ring_buffer.hh
|
|
||||||
common/sequencer.hh
|
|
||||||
common/squashed_history.hh
|
|
||||||
common/task.hh
|
|
||||||
common/thread.hh
|
|
||||||
common/timed_queue.hh
|
|
||||||
common/util_algorithm.hh
|
|
||||||
common/util_string.hh
|
|
||||||
common/value.hh
|
|
||||||
common/yas_enum.hh
|
|
||||||
common/yas_imgui.hh
|
|
||||||
common/yas_imnodes.hh
|
|
||||||
common/yas_nf7.hh
|
|
||||||
common/yas_std_atomic.hh
|
|
||||||
common/yas_std_filesystem.hh
|
|
||||||
common/yas_std_variant.hh
|
|
||||||
|
|
||||||
$<$<PLATFORM_ID:Linux>:common/nfile_unix.cc>
|
|
||||||
$<$<PLATFORM_ID:Windows>:common/nfile_win.cc>
|
|
||||||
|
|
||||||
file/audio_context.cc
|
|
||||||
file/audio_device.cc
|
|
||||||
file/luajit_context.cc
|
|
||||||
file/luajit_inline_node.cc
|
|
||||||
file/luajit_node.cc
|
|
||||||
file/node_imm.cc
|
|
||||||
file/node_network.cc
|
|
||||||
file/node_ref.cc
|
|
||||||
file/sequencer_adaptor.cc
|
|
||||||
file/sequencer_call.cc
|
|
||||||
file/sequencer_timeline.cc
|
|
||||||
file/system_call.cc
|
|
||||||
file/system_dir.cc
|
|
||||||
file/system_event.cc
|
|
||||||
file/system_imgui.cc
|
|
||||||
file/system_logger.cc
|
|
||||||
file/system_nfile.cc
|
|
||||||
file/value_curve.cc
|
|
||||||
file/value_plot.cc
|
|
||||||
)
|
|
||||||
target_link_libraries(nf7
|
target_link_libraries(nf7
|
||||||
PRIVATE
|
PRIVATE
|
||||||
glew
|
nf7if
|
||||||
glfw
|
nf7core
|
||||||
imgui
|
|
||||||
imnodes
|
|
||||||
implot
|
|
||||||
linalg.h
|
|
||||||
luajit
|
|
||||||
magic_enum
|
|
||||||
miniaudio
|
|
||||||
source_location
|
|
||||||
yas
|
|
||||||
yaml-cpp
|
|
||||||
)
|
)
|
||||||
|
13
LICENSE
13
LICENSE
@ -1,13 +0,0 @@
|
|||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
Version 2, December 2004
|
|
||||||
|
|
||||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
|
||||||
|
|
||||||
Everyone is permitted to copy and distribute verbatim or modified
|
|
||||||
copies of this license document, and changing it is allowed as long
|
|
||||||
as the name is changed.
|
|
||||||
|
|
||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
|
|
||||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
17
README.md
17
README.md
@ -1,8 +1,19 @@
|
|||||||
nf7
|
Nf7
|
||||||
====
|
====
|
||||||
|
|
||||||
node-based programming language
|
Nf7 is an abstraction layer for Media-programming.
|
||||||
|
The goal is to allow creative activities on any platforms without any differences.
|
||||||
|
|
||||||
## LICENSE
|
**UNDER REFACTORING:** Please checkout tag v0.4.1 to see a codebase which works.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ cmake -B build
|
||||||
|
$ cmake --build build -j4
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
WTFPLv2
|
WTFPLv2
|
||||||
|
|
||||||
|
@ -1,79 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <span>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "common/history.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class AggregateCommand : public nf7::History::Command {
|
|
||||||
public:
|
|
||||||
using CommandList = std::vector<std::unique_ptr<Command>>;
|
|
||||||
|
|
||||||
AggregateCommand(CommandList&& commands, bool applied = false) noexcept :
|
|
||||||
commands_(std::move(commands)), applied_(applied) {
|
|
||||||
}
|
|
||||||
~AggregateCommand() noexcept {
|
|
||||||
if (applied_) {
|
|
||||||
for (auto itr = commands_.begin(); itr < commands_.end(); ++itr) {
|
|
||||||
*itr = nullptr;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (auto itr = commands_.rbegin(); itr < commands_.rend(); ++itr) {
|
|
||||||
*itr = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Apply() override {
|
|
||||||
Exec(commands_.begin(), commands_.end(),
|
|
||||||
[](auto& a) { a->Apply(); },
|
|
||||||
[](auto& a) { a->Revert(); });
|
|
||||||
applied_ = true;
|
|
||||||
}
|
|
||||||
void Revert() override {
|
|
||||||
Exec(commands_.rbegin(), commands_.rend(),
|
|
||||||
[](auto& a) { a->Revert(); },
|
|
||||||
[](auto& a) { a->Apply(); });
|
|
||||||
applied_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::span<const std::unique_ptr<Command>> commands() const noexcept { return commands_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
CommandList commands_;
|
|
||||||
|
|
||||||
bool applied_;
|
|
||||||
|
|
||||||
|
|
||||||
static void Exec(auto begin, auto end, const auto& apply, const auto& revert) {
|
|
||||||
auto itr = begin;
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
while (itr < end) {
|
|
||||||
apply(*itr);
|
|
||||||
++itr;
|
|
||||||
}
|
|
||||||
} catch (History::CorruptException&) {
|
|
||||||
throw History::CorruptException("failed to revert AggregateCommand");
|
|
||||||
}
|
|
||||||
} catch (History::CorruptException&) {
|
|
||||||
try {
|
|
||||||
while (itr > begin) {
|
|
||||||
--itr;
|
|
||||||
revert(*itr);
|
|
||||||
}
|
|
||||||
} catch (History::CorruptException&) {
|
|
||||||
throw History::CorruptException(
|
|
||||||
"AggregateCommand gave up recovering from failure of revert");
|
|
||||||
}
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,31 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <miniaudio.h>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::audio {
|
|
||||||
|
|
||||||
class Queue : public nf7::File::Interface {
|
|
||||||
public:
|
|
||||||
using Task = std::function<void(ma_context*)>;
|
|
||||||
|
|
||||||
Queue() = default;
|
|
||||||
virtual ~Queue() = default;
|
|
||||||
Queue(const Queue&) = delete;
|
|
||||||
Queue(Queue&&) = delete;
|
|
||||||
Queue& operator=(const Queue&) = delete;
|
|
||||||
Queue& operator=(Queue&&) = delete;
|
|
||||||
|
|
||||||
// thread-safe
|
|
||||||
// WARNING: when failed to create ma_context, nullptr is passed
|
|
||||||
virtual void Push(const std::shared_ptr<nf7::Context>&, Task&&) noexcept = 0;
|
|
||||||
|
|
||||||
virtual std::shared_ptr<Queue> self() noexcept = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7::audio
|
|
@ -1,27 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class Dir : public File::Interface {
|
|
||||||
public:
|
|
||||||
class DuplicateException;
|
|
||||||
|
|
||||||
Dir() = default;
|
|
||||||
|
|
||||||
virtual File& Add(std::string_view, std::unique_ptr<File>&&) = 0;
|
|
||||||
virtual std::unique_ptr<File> Remove(std::string_view) noexcept = 0;
|
|
||||||
};
|
|
||||||
class Dir::DuplicateException : public Exception {
|
|
||||||
public:
|
|
||||||
using Exception::Exception;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,42 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class DirItem : public File::Interface {
|
|
||||||
public:
|
|
||||||
enum Flag : uint8_t {
|
|
||||||
kNone = 0,
|
|
||||||
kTree = 1 << 0,
|
|
||||||
kMenu = 1 << 1,
|
|
||||||
kTooltip = 1 << 2,
|
|
||||||
kWidget = 1 << 3,
|
|
||||||
kDragDropTarget = 1 << 4,
|
|
||||||
};
|
|
||||||
using Flags = uint8_t;
|
|
||||||
|
|
||||||
DirItem() = delete;
|
|
||||||
DirItem(Flags flags) noexcept : flags_(flags) {
|
|
||||||
}
|
|
||||||
DirItem(const DirItem&) = delete;
|
|
||||||
DirItem(DirItem&&) = delete;
|
|
||||||
DirItem& operator=(const DirItem&) = delete;
|
|
||||||
DirItem& operator=(DirItem&&) = delete;
|
|
||||||
|
|
||||||
virtual void UpdateTree() noexcept { }
|
|
||||||
virtual void UpdateMenu() noexcept { }
|
|
||||||
virtual void UpdateTooltip() noexcept { }
|
|
||||||
virtual void UpdateWidget() noexcept { }
|
|
||||||
virtual void UpdateDragDropTarget() noexcept { }
|
|
||||||
|
|
||||||
Flags flags() const noexcept { return flags_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
Flags flags_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,62 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string_view>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class FileBase : public nf7::File {
|
|
||||||
public:
|
|
||||||
class Feature {
|
|
||||||
public:
|
|
||||||
Feature() = default;
|
|
||||||
virtual ~Feature() = default;
|
|
||||||
Feature(const Feature&) = delete;
|
|
||||||
Feature(Feature&&) = delete;
|
|
||||||
Feature& operator=(const Feature&) = delete;
|
|
||||||
Feature& operator=(Feature&&) = delete;
|
|
||||||
|
|
||||||
// Feature* is just for avoiding multi inheritance issues with Env::Watcher
|
|
||||||
virtual nf7::File* Find(std::string_view) const noexcept { return nullptr; }
|
|
||||||
virtual void Handle(const nf7::File::Event&) noexcept { }
|
|
||||||
virtual void Update() noexcept { }
|
|
||||||
};
|
|
||||||
|
|
||||||
FileBase(const nf7::File::TypeInfo& t,
|
|
||||||
nf7::Env& env,
|
|
||||||
std::vector<Feature*>&& feats = {}) noexcept :
|
|
||||||
nf7::File(t, env), feats_(std::move(feats)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
nf7::File* Find(std::string_view name) const noexcept override {
|
|
||||||
for (auto feat : feats_) {
|
|
||||||
if (auto ret = feat->Find(name)) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
void Handle(const nf7::File::Event& ev) noexcept override {
|
|
||||||
for (auto feat : feats_) {
|
|
||||||
feat->Handle(ev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Update() noexcept override {
|
|
||||||
for (auto feat : feats_) {
|
|
||||||
feat->Update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void Install(Feature& f) noexcept {
|
|
||||||
feats_.push_back(&f);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<Feature*> feats_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,134 +0,0 @@
|
|||||||
#include "common/file_holder.hh"
|
|
||||||
|
|
||||||
#include <imgui.h>
|
|
||||||
|
|
||||||
|
|
||||||
using namespace std::literals;
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
nf7::File* FileHolder::Find(std::string_view name) const noexcept {
|
|
||||||
return name == id_? file_: nullptr;
|
|
||||||
}
|
|
||||||
void FileHolder::Handle(const nf7::File::Event& ev) noexcept {
|
|
||||||
switch (ev.type) {
|
|
||||||
case nf7::File::Event::kAdd:
|
|
||||||
SetUp();
|
|
||||||
break;
|
|
||||||
case nf7::File::Event::kRemove:
|
|
||||||
TearDown();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void FileHolder::Update() noexcept {
|
|
||||||
if (own()) {
|
|
||||||
ImGui::PushID(this);
|
|
||||||
file_->Update();
|
|
||||||
ImGui::PopID();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileHolder::SetUp() noexcept {
|
|
||||||
const bool first_setup = !file_;
|
|
||||||
|
|
||||||
if (own()) {
|
|
||||||
file_ = std::get<std::shared_ptr<nf7::File>>(entity_).get();
|
|
||||||
if (owner_->id() && file_->id() == 0) {
|
|
||||||
file_->MoveUnder(*owner_, id_);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (ref()) {
|
|
||||||
if (owner_->id()) {
|
|
||||||
try {
|
|
||||||
file_ = &owner_->ResolveOrThrow(path());
|
|
||||||
} catch (nf7::File::NotFoundException&) {
|
|
||||||
file_ = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file_) {
|
|
||||||
auto mem = own()? file_->interface<nf7::Memento>(): nullptr;
|
|
||||||
|
|
||||||
// init watcher
|
|
||||||
if (file_->id() && !watcher_) {
|
|
||||||
watcher_.emplace(file_->env());
|
|
||||||
watcher_->Watch(file_->id());
|
|
||||||
|
|
||||||
watcher_->AddHandler(nf7::File::Event::kUpdate, [this, mem](auto&) {
|
|
||||||
if (mem) {
|
|
||||||
auto ptag = std::exchange(tag_, mem->Save());
|
|
||||||
if (ptag != tag_) {
|
|
||||||
onChildMementoChange();
|
|
||||||
if (mem_) mem_->Commit(); // commit owner's memento
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onChildUpdate();
|
|
||||||
owner_->Touch();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// memento setup
|
|
||||||
if (first_setup && mem) {
|
|
||||||
if (!tag_) {
|
|
||||||
tag_ = mem->Save();
|
|
||||||
} else {
|
|
||||||
mem->Restore(tag_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void FileHolder::TearDown() noexcept {
|
|
||||||
if (!owner_->id()) return;
|
|
||||||
if (own()) {
|
|
||||||
file_->Isolate();
|
|
||||||
}
|
|
||||||
file_ = nullptr;
|
|
||||||
watcher_ = std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
FileHolder::Tag::Tag(const Tag& src) noexcept {
|
|
||||||
if (src.target_) {
|
|
||||||
entity_ = src.target_->entity_;
|
|
||||||
tag_ = src.target_->tag_;
|
|
||||||
} else {
|
|
||||||
entity_ = src.entity_;
|
|
||||||
tag_ = src.tag_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FileHolder::Tag& FileHolder::Tag::operator=(const Tag& src) noexcept {
|
|
||||||
if (!src.target_ && target_) {
|
|
||||||
// restore
|
|
||||||
target_->TearDown();
|
|
||||||
target_->entity_ = src.entity_;
|
|
||||||
target_->tag_ = src.tag_;
|
|
||||||
target_->SetUp();
|
|
||||||
} else if (!src.target_ && !target_) {
|
|
||||||
// shallow copy
|
|
||||||
entity_ = src.entity_;
|
|
||||||
tag_ = src.tag_;
|
|
||||||
} else {
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
void FileHolder::Tag::SetTarget(nf7::FileHolder& h) noexcept {
|
|
||||||
assert(!target_);
|
|
||||||
|
|
||||||
target_ = &h;
|
|
||||||
|
|
||||||
h.TearDown();
|
|
||||||
if (std::holds_alternative<nf7::File::Path>(entity_)) {
|
|
||||||
h.Emplace(std::move(std::get<nf7::File::Path>(entity_)));
|
|
||||||
} else if (std::holds_alternative<std::shared_ptr<nf7::File>>(entity_)) {
|
|
||||||
h.Emplace(std::get<std::shared_ptr<nf7::File>>(entity_)->Clone(h.env()));
|
|
||||||
}
|
|
||||||
entity_ = std::monostate {};
|
|
||||||
tag_ = nullptr;
|
|
||||||
h.SetUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,188 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
#include <yas/serialize.hpp>
|
|
||||||
#include <yas/types/std/variant.hpp>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
#include "common/file_base.hh"
|
|
||||||
#include "common/generic_watcher.hh"
|
|
||||||
#include "common/memento.hh"
|
|
||||||
#include "common/mutable_memento.hh"
|
|
||||||
#include "common/yas_nf7.hh"
|
|
||||||
#include "common/yas_std_variant.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class FileHolder : public nf7::FileBase::Feature {
|
|
||||||
public:
|
|
||||||
class Tag;
|
|
||||||
|
|
||||||
class EmptyException final : public nf7::Exception {
|
|
||||||
public:
|
|
||||||
using nf7::Exception::Exception;
|
|
||||||
};
|
|
||||||
|
|
||||||
using Entity = std::variant<
|
|
||||||
std::monostate, nf7::File::Path, std::shared_ptr<nf7::File>>;
|
|
||||||
|
|
||||||
FileHolder(nf7::File& owner, std::string_view id,
|
|
||||||
nf7::MutableMemento* mem = nullptr) noexcept :
|
|
||||||
owner_(&owner), mem_(mem), id_(id) {
|
|
||||||
}
|
|
||||||
FileHolder(nf7::File& owner, std::string_view id,
|
|
||||||
nf7::MutableMemento& mem) noexcept :
|
|
||||||
FileHolder(owner, id, &mem) {
|
|
||||||
}
|
|
||||||
FileHolder(const FileHolder&) = delete;
|
|
||||||
FileHolder(FileHolder&&) = delete;
|
|
||||||
FileHolder& operator=(const FileHolder&) = delete;
|
|
||||||
FileHolder& operator=(FileHolder&&) = delete;
|
|
||||||
|
|
||||||
void Serialize(nf7::Serializer& ar) const {
|
|
||||||
ar(entity_);
|
|
||||||
}
|
|
||||||
void Deserialize(nf7::Deserializer& ar) {
|
|
||||||
try {
|
|
||||||
ar(entity_);
|
|
||||||
} catch (nf7::Exception&) {
|
|
||||||
entity_ = std::monostate {};
|
|
||||||
ar.env().Throw(std::current_exception());
|
|
||||||
}
|
|
||||||
SetUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Emplace(nf7::File::Path&& path) noexcept {
|
|
||||||
TearDown();
|
|
||||||
tag_ = nullptr;
|
|
||||||
entity_ = std::move(path);
|
|
||||||
SetUp();
|
|
||||||
|
|
||||||
onEmplace();
|
|
||||||
if (mem_) mem_->Commit();
|
|
||||||
}
|
|
||||||
void Emplace(std::unique_ptr<nf7::File>&& f) noexcept {
|
|
||||||
TearDown();
|
|
||||||
tag_ = nullptr;
|
|
||||||
entity_ = std::move(f);
|
|
||||||
SetUp();
|
|
||||||
|
|
||||||
onEmplace();
|
|
||||||
if (mem_) mem_->Commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
nf7::File& GetFileOrThrow() {
|
|
||||||
if (auto f = GetFile()) {
|
|
||||||
return *f;
|
|
||||||
}
|
|
||||||
throw EmptyException {"holder is empty"};
|
|
||||||
}
|
|
||||||
nf7::File* GetFile() noexcept {
|
|
||||||
SetUp();
|
|
||||||
return file_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// nf7::FileBase::Feature methods
|
|
||||||
nf7::File* Find(std::string_view name) const noexcept override;
|
|
||||||
void Handle(const nf7::File::Event&) noexcept override;
|
|
||||||
void Update() noexcept override;
|
|
||||||
|
|
||||||
bool own() const noexcept {
|
|
||||||
return std::holds_alternative<std::shared_ptr<nf7::File>>(entity_);
|
|
||||||
}
|
|
||||||
bool ref() const noexcept {
|
|
||||||
return std::holds_alternative<nf7::File::Path>(entity_);
|
|
||||||
}
|
|
||||||
bool empty() const noexcept {
|
|
||||||
return std::holds_alternative<std::monostate>(entity_);
|
|
||||||
}
|
|
||||||
|
|
||||||
nf7::File& owner() const noexcept { return *owner_; }
|
|
||||||
nf7::Env& env() const noexcept { return owner_->env(); }
|
|
||||||
const std::string& id() const noexcept { return id_; }
|
|
||||||
|
|
||||||
nf7::File* file() const noexcept { return file_; }
|
|
||||||
nf7::File::Path path() const noexcept {
|
|
||||||
assert(!empty());
|
|
||||||
return own()? nf7::File::Path {{id_}}: std::get<nf7::File::Path>(entity_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// called when kUpdate event is happened on the child
|
|
||||||
std::function<void(void)> onChildUpdate = [](){};
|
|
||||||
|
|
||||||
// called when the child's memento tag id is changed
|
|
||||||
std::function<void(void)> onChildMementoChange = [](){};
|
|
||||||
|
|
||||||
// called right before returning from Emplace()
|
|
||||||
std::function<void(void)> onEmplace = [](){};
|
|
||||||
|
|
||||||
private:
|
|
||||||
nf7::File* const owner_;
|
|
||||||
nf7::MutableMemento* const mem_;
|
|
||||||
|
|
||||||
const std::string id_;
|
|
||||||
|
|
||||||
Entity entity_;
|
|
||||||
std::shared_ptr<nf7::Memento::Tag> tag_;
|
|
||||||
|
|
||||||
nf7::File* file_ = nullptr;
|
|
||||||
|
|
||||||
std::optional<nf7::GenericWatcher> watcher_;
|
|
||||||
|
|
||||||
|
|
||||||
void SetUp() noexcept;
|
|
||||||
void TearDown() noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
// to save/restore FileHolder's changes through GenericMemento
|
|
||||||
class FileHolder::Tag final {
|
|
||||||
public:
|
|
||||||
Tag() = default;
|
|
||||||
Tag(const Tag&) noexcept;
|
|
||||||
Tag& operator=(const Tag&) noexcept;
|
|
||||||
Tag(Tag&&) = default;
|
|
||||||
Tag& operator=(Tag&&) = default;
|
|
||||||
|
|
||||||
void SetTarget(nf7::FileHolder& h) noexcept;
|
|
||||||
|
|
||||||
private:
|
|
||||||
nf7::FileHolder* target_ = nullptr;
|
|
||||||
|
|
||||||
Entity entity_;
|
|
||||||
std::shared_ptr<nf7::Memento::Tag> tag_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
||||||
|
|
||||||
|
|
||||||
namespace yas::detail {
|
|
||||||
|
|
||||||
template <size_t F>
|
|
||||||
struct serializer<
|
|
||||||
type_prop::not_a_fundamental,
|
|
||||||
ser_case::use_internal_serializer,
|
|
||||||
F,
|
|
||||||
nf7::FileHolder> {
|
|
||||||
public:
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& save(Archive& ar, const nf7::FileHolder& h) {
|
|
||||||
h.Serialize(ar);
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& load(Archive& ar, nf7::FileHolder& h) {
|
|
||||||
h.Deserialize(ar);
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace yas::detail
|
|
362
common/future.hh
362
common/future.hh
@ -1,362 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <coroutine>
|
|
||||||
#include <exception>
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <optional>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <utility>
|
|
||||||
#include <variant>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
#include "common/generic_context.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
// How To Use (factory side)
|
|
||||||
// 1. Create Future<T>::Promise. (T is a type of returned value)
|
|
||||||
// 2. Get Future<T> from Future<T>::Promise and Pass it to ones who want to get T.
|
|
||||||
// 3. Call Promise::Return(T) or Promise::Throw() to finish the promise.
|
|
||||||
//
|
|
||||||
// Users who receive Future can wait for finishing
|
|
||||||
// by Future::Then(), Future::ThenIf(), Future::Catch(), or co_await.
|
|
||||||
|
|
||||||
|
|
||||||
class CoroutineAbortException final : public nf7::Exception {
|
|
||||||
public:
|
|
||||||
using nf7::Exception::Exception;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class Future final {
|
|
||||||
public:
|
|
||||||
class Promise;
|
|
||||||
class Coro;
|
|
||||||
|
|
||||||
using ThisFuture = nf7::Future<T>;
|
|
||||||
using Handle = std::coroutine_handle<Promise>;
|
|
||||||
using Imm = std::variant<T, std::exception_ptr>;
|
|
||||||
|
|
||||||
enum State { kYet, kDone, kError, };
|
|
||||||
|
|
||||||
// A data shared between Future, Promise, and Coro.
|
|
||||||
// One per one Promise.
|
|
||||||
struct Data final {
|
|
||||||
public:
|
|
||||||
std::weak_ptr<nf7::Context> ctx;
|
|
||||||
|
|
||||||
std::atomic<bool> destroyed = false;
|
|
||||||
std::atomic<bool> aborted = false;
|
|
||||||
std::atomic<size_t> pros = 0;
|
|
||||||
std::atomic<State> state = kYet;
|
|
||||||
|
|
||||||
std::mutex mtx;
|
|
||||||
std::optional<T> value;
|
|
||||||
std::exception_ptr exception;
|
|
||||||
std::vector<std::function<void()>> recv;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Factory side have this to tell finish or abort.
|
|
||||||
class Promise final {
|
|
||||||
public:
|
|
||||||
// Use data_() instead, MSVC can't understand the followings:
|
|
||||||
// template <typename U> friend class nf7::Future<U>;
|
|
||||||
// template <typename U> friend class nf7::Future<U>::Coro;
|
|
||||||
|
|
||||||
static constexpr bool kThisIsNf7FuturePromise = true;
|
|
||||||
|
|
||||||
Promise() noexcept : data_(std::make_shared<Data>()) {
|
|
||||||
++data_->pros;
|
|
||||||
}
|
|
||||||
Promise(const std::shared_ptr<nf7::Context>& ctx) noexcept : Promise() {
|
|
||||||
data_->ctx = ctx;
|
|
||||||
}
|
|
||||||
Promise(const Promise& src) noexcept : data_(src.data_) {
|
|
||||||
++data_->pros;
|
|
||||||
}
|
|
||||||
Promise(Promise&&) = default;
|
|
||||||
Promise& operator=(const Promise& src) noexcept {
|
|
||||||
data_ = src.data_;
|
|
||||||
++data_->pros;
|
|
||||||
}
|
|
||||||
Promise& operator=(Promise&&) = default;
|
|
||||||
~Promise() noexcept {
|
|
||||||
if (data_ && --data_->pros == 0 && data_->state == kYet) {
|
|
||||||
Throw(std::make_exception_ptr<nf7::Exception>({"promise forgotten"}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// thread-safe
|
|
||||||
auto Return(T&& v) noexcept {
|
|
||||||
std::unique_lock<std::mutex> k(data_->mtx);
|
|
||||||
if (data_->state == kYet) {
|
|
||||||
data_->value = std::move(v);
|
|
||||||
data_->state = kDone;
|
|
||||||
CallReceivers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto Return(const T& v) noexcept {
|
|
||||||
Return(T {v});
|
|
||||||
}
|
|
||||||
// thread-safe
|
|
||||||
void Throw(std::exception_ptr e) noexcept {
|
|
||||||
std::unique_lock<std::mutex> k(data_->mtx);
|
|
||||||
if (data_->state == kYet) {
|
|
||||||
data_->exception = e;
|
|
||||||
data_->state = kError;
|
|
||||||
CallReceivers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template <typename E, typename... Args>
|
|
||||||
void Throw(Args&&... args) noexcept {
|
|
||||||
return Throw(std::make_exception_ptr<E>(E {std::forward<Args>(args)...}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// thread-safe
|
|
||||||
// Do Return(f()) if no exception is thrown, otherwise call Throw().
|
|
||||||
auto Wrap(const std::function<T()>& f) noexcept
|
|
||||||
try {
|
|
||||||
Return(f());
|
|
||||||
} catch (...) {
|
|
||||||
Throw(std::current_exception());
|
|
||||||
}
|
|
||||||
|
|
||||||
// thread-safe
|
|
||||||
// Creates Future() object.
|
|
||||||
ThisFuture future() const noexcept {
|
|
||||||
assert(data_);
|
|
||||||
return ThisFuture(data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto get_return_object() noexcept {
|
|
||||||
return Coro(Handle::from_promise(*this), data_);
|
|
||||||
}
|
|
||||||
auto initial_suspend() const noexcept {
|
|
||||||
return std::suspend_always();
|
|
||||||
}
|
|
||||||
auto final_suspend() const noexcept {
|
|
||||||
return std::suspend_always();
|
|
||||||
}
|
|
||||||
auto yield_value(const T& v) {
|
|
||||||
Return(T(v));
|
|
||||||
return std::suspend_never();
|
|
||||||
}
|
|
||||||
auto yield_value(T&& v) {
|
|
||||||
Return(std::move(v));
|
|
||||||
return std::suspend_never();
|
|
||||||
}
|
|
||||||
auto return_void() {
|
|
||||||
if (data_->state == kYet) {
|
|
||||||
if constexpr (std::is_same<T, std::monostate>::value) {
|
|
||||||
Return({});
|
|
||||||
} else {
|
|
||||||
assert(false && "coroutine returned without value");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto unhandled_exception() noexcept {
|
|
||||||
Throw(std::current_exception());
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::shared_ptr<Data>& data__() noexcept { return data_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<Data> data_;
|
|
||||||
|
|
||||||
void CallReceivers() noexcept {
|
|
||||||
for (auto recv : data_->recv) recv();
|
|
||||||
data_->recv.clear();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Define a function returning Coro to implement a factory with coroutine.
|
|
||||||
class Coro final {
|
|
||||||
public:
|
|
||||||
friend Promise;
|
|
||||||
using promise_type = Promise;
|
|
||||||
|
|
||||||
Coro() = delete;
|
|
||||||
~Coro() noexcept {
|
|
||||||
if (data_ && !data_->destroyed.exchange(true)) {
|
|
||||||
h_.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Coro(const Coro&) = delete;
|
|
||||||
Coro(Coro&&) = default;
|
|
||||||
Coro& operator=(const Coro&) = delete;
|
|
||||||
Coro& operator=(Coro&&) = default;
|
|
||||||
|
|
||||||
ThisFuture Start(const std::shared_ptr<nf7::Context>& ctx) noexcept {
|
|
||||||
ctx->env().ExecSub(ctx, [h = h_]() { h.resume(); });
|
|
||||||
data_->ctx = ctx;
|
|
||||||
return ThisFuture(data_);
|
|
||||||
}
|
|
||||||
void Abort() noexcept {
|
|
||||||
h_.promise().Throw(
|
|
||||||
std::make_exception_ptr<CoroutineAbortException>({"coroutine aborted"}));
|
|
||||||
data_->aborted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Handle h_;
|
|
||||||
std::shared_ptr<Data> data_;
|
|
||||||
|
|
||||||
Coro(Handle h, const std::shared_ptr<Data>& data) noexcept : h_(h), data_(data) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
Future(const T& v) noexcept : imm_({v}) {
|
|
||||||
}
|
|
||||||
Future(T&& v) noexcept : imm_({std::move(v)}) {
|
|
||||||
}
|
|
||||||
Future(std::exception_ptr e) noexcept : imm_({e}) {
|
|
||||||
}
|
|
||||||
Future(const Imm& imm) noexcept : imm_(imm) {
|
|
||||||
}
|
|
||||||
Future(Imm&& imm) noexcept : imm_(std::move(imm)) {
|
|
||||||
}
|
|
||||||
Future(const ThisFuture&) = default;
|
|
||||||
Future(ThisFuture&&) = default;
|
|
||||||
Future& operator=(const ThisFuture&) = default;
|
|
||||||
Future& operator=(ThisFuture&&) = default;
|
|
||||||
|
|
||||||
// Schedules to execute f() immediately on any thread
|
|
||||||
// when the promise is finished or aborted.
|
|
||||||
// If ctx is not nullptr, the function will be run synchronized with main thread.
|
|
||||||
ThisFuture& Then(const std::shared_ptr<nf7::Context>& ctx, std::function<void(const ThisFuture&)>&& f) noexcept {
|
|
||||||
auto fun = std::move(f);
|
|
||||||
if (ctx) {
|
|
||||||
fun = [ctx, fun = std::move(fun)](auto& fu) {
|
|
||||||
ctx->env().ExecSub(
|
|
||||||
ctx, [fu, fun = std::move(fun)]() mutable { fun(fu); });
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (data_) {
|
|
||||||
std::unique_lock<std::mutex> k(data_->mtx);
|
|
||||||
if (yet()) {
|
|
||||||
data_->recv.push_back(
|
|
||||||
[fun = std::move(fun), d = data_]() mutable { fun(ThisFuture {d}); });
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fun(*this);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
template <typename R>
|
|
||||||
nf7::Future<R> Then(const std::shared_ptr<nf7::Context>& ctx,
|
|
||||||
std::function<void(const ThisFuture&, typename nf7::Future<R>::Promise&)>&& f) noexcept {
|
|
||||||
typename nf7::Future<R>::Promise pro;
|
|
||||||
Then(ctx, [pro, f = std::move(f)](auto& fu) mutable {
|
|
||||||
try {
|
|
||||||
f(fu, pro);
|
|
||||||
} catch (...) {
|
|
||||||
pro.Throw(std::current_exception());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return pro.future();
|
|
||||||
}
|
|
||||||
ThisFuture& Then(auto&& f) noexcept {
|
|
||||||
return Then(nullptr, std::move(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
// same as Then() but called when it's done without error
|
|
||||||
ThisFuture& ThenIf(const std::shared_ptr<nf7::Context>& ctx, std::function<void(const T&)>&& f) noexcept {
|
|
||||||
Then(ctx, [f = std::move(f)](auto& fu) {
|
|
||||||
if (fu.done()) f(fu.value());
|
|
||||||
});
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
ThisFuture& ThenIf(auto&& f) noexcept {
|
|
||||||
return ThenIf(nullptr, std::move(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
// same as Then() but called when it caused an exception
|
|
||||||
template <typename E>
|
|
||||||
ThisFuture& Catch(const std::shared_ptr<nf7::Context>& ctx, std::function<void(E&)>&& f) noexcept {
|
|
||||||
Then(ctx, [f = std::move(f)](auto& fu) {
|
|
||||||
try { fu.value(); } catch (E& e) { f(e); } catch (...) { }
|
|
||||||
});
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
template <typename E>
|
|
||||||
ThisFuture& Catch(auto&& f) noexcept {
|
|
||||||
return Catch<E>(nullptr, std::move(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& value() const {
|
|
||||||
if (imm_) {
|
|
||||||
if (std::holds_alternative<T>(*imm_)) return std::get<T>(*imm_);
|
|
||||||
std::rethrow_exception(std::get<std::exception_ptr>(*imm_));
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(data_);
|
|
||||||
switch (data_->state) {
|
|
||||||
case kYet:
|
|
||||||
assert(false);
|
|
||||||
break;
|
|
||||||
case kDone:
|
|
||||||
return *data_->value;
|
|
||||||
case kError:
|
|
||||||
std::rethrow_exception(data_->exception);
|
|
||||||
}
|
|
||||||
throw 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool yet() const noexcept {
|
|
||||||
return !imm_ && data_->state == kYet;
|
|
||||||
}
|
|
||||||
bool done() const noexcept {
|
|
||||||
return (imm_ && std::holds_alternative<T>(*imm_)) || data_->state == kDone;
|
|
||||||
}
|
|
||||||
bool error() const noexcept {
|
|
||||||
return (imm_ && std::holds_alternative<std::exception_ptr>(*imm_)) ||
|
|
||||||
data_->state == kError;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool await_ready() const noexcept { return !yet(); }
|
|
||||||
template <typename U>
|
|
||||||
void await_suspend(std::coroutine_handle<U> caller) const noexcept {
|
|
||||||
static_assert(U::kThisIsNf7FuturePromise, "illegal coroutine");
|
|
||||||
assert(data_);
|
|
||||||
auto& data = *data_;
|
|
||||||
|
|
||||||
std::unique_lock<std::mutex> k(data.mtx);
|
|
||||||
auto callee_ctx = data.ctx.lock();
|
|
||||||
|
|
||||||
auto caller_data = caller.promise().data__();
|
|
||||||
auto caller_ctx = caller_data->ctx.lock();
|
|
||||||
assert(caller_ctx);
|
|
||||||
|
|
||||||
if (yet()) {
|
|
||||||
data.recv.push_back([caller, caller_data, caller_ctx, callee_ctx]() {
|
|
||||||
caller_ctx->env().ExecSub(caller_ctx, [caller, caller_data, caller_ctx]() {
|
|
||||||
if (!caller_data->aborted) {
|
|
||||||
caller.resume();
|
|
||||||
} else {
|
|
||||||
if (!caller_data->destroyed.exchange(true)) {
|
|
||||||
caller.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// promise has ended after await_ready() is called
|
|
||||||
caller.resume();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto& await_resume() { return value(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::optional<Imm> imm_;
|
|
||||||
std::shared_ptr<Data> data_;
|
|
||||||
|
|
||||||
Future(const std::shared_ptr<Data>& data) noexcept : data_(data) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,50 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class GenericContext : public Context {
|
|
||||||
public:
|
|
||||||
GenericContext(Env& env, File::Id id, std::string_view desc = "") noexcept :
|
|
||||||
Context(env, id), desc_(desc) {
|
|
||||||
}
|
|
||||||
GenericContext(File& f, std::string_view desc = "") noexcept :
|
|
||||||
GenericContext(f.env(), f.id(), desc) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void CleanUp() noexcept override {
|
|
||||||
}
|
|
||||||
void Abort() noexcept override {
|
|
||||||
abort_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GetMemoryUsage() const noexcept override {
|
|
||||||
return mem_;
|
|
||||||
}
|
|
||||||
std::string GetDescription() const noexcept override {
|
|
||||||
return desc_;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t& memoryUsage() noexcept { return mem_; }
|
|
||||||
std::string& description() noexcept { return desc_; }
|
|
||||||
|
|
||||||
bool aborted() const noexcept { return abort_; }
|
|
||||||
size_t memoryUsage() const noexcept { return mem_; }
|
|
||||||
const std::string& description() const noexcept { return desc_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::atomic<bool> abort_ = false;
|
|
||||||
|
|
||||||
size_t mem_;
|
|
||||||
|
|
||||||
std::string desc_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,65 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <memory>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "common/history.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class GenericHistory : public nf7::History {
|
|
||||||
public:
|
|
||||||
GenericHistory() = default;
|
|
||||||
GenericHistory(const GenericHistory&) = delete;
|
|
||||||
GenericHistory(GenericHistory&&) = default;
|
|
||||||
GenericHistory& operator=(const GenericHistory&) = delete;
|
|
||||||
GenericHistory& operator=(GenericHistory&&) = default;
|
|
||||||
~GenericHistory() noexcept {
|
|
||||||
Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
Command& Add(std::unique_ptr<Command>&& cmd) noexcept override {
|
|
||||||
cmds_.erase(cmds_.begin()+static_cast<intmax_t>(cursor_), cmds_.end());
|
|
||||||
cmds_.push_back(std::move(cmd));
|
|
||||||
cursor_++;
|
|
||||||
return *cmds_.back();
|
|
||||||
}
|
|
||||||
void Clear() noexcept {
|
|
||||||
for (size_t i = 0; i < cursor_; ++i) {
|
|
||||||
cmds_[i] = nullptr;
|
|
||||||
}
|
|
||||||
for (size_t i = cmds_.size(); i > cursor_;) {
|
|
||||||
--i;
|
|
||||||
cmds_[i] = nullptr;
|
|
||||||
}
|
|
||||||
cmds_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnDo() {
|
|
||||||
if (cursor_ <= 0) return;
|
|
||||||
cmds_[cursor_-1]->Revert();
|
|
||||||
--cursor_;
|
|
||||||
}
|
|
||||||
void ReDo() {
|
|
||||||
if (cursor_ >= cmds_.size()) return;
|
|
||||||
cmds_[cursor_]->Apply();
|
|
||||||
++cursor_;
|
|
||||||
}
|
|
||||||
|
|
||||||
Command* prev() const noexcept {
|
|
||||||
return cursor_ > 0? cmds_[cursor_-1].get(): nullptr;
|
|
||||||
}
|
|
||||||
Command* next() const noexcept {
|
|
||||||
return cursor_ < cmds_.size()? cmds_[cursor_].get(): nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<std::unique_ptr<Command>> cmds_;
|
|
||||||
|
|
||||||
size_t cursor_ = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,108 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <memory>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
#include "common/mutable_memento.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class GenericMemento : public nf7::MutableMemento {
|
|
||||||
public:
|
|
||||||
class CustomTag;
|
|
||||||
|
|
||||||
GenericMemento(T&& data, nf7::File* f = nullptr) noexcept :
|
|
||||||
file_(f), initial_(T(data)), data_(std::move(data)) {
|
|
||||||
}
|
|
||||||
GenericMemento(T&& data, nf7::File& f) noexcept :
|
|
||||||
GenericMemento(std::move(data), &f) {
|
|
||||||
}
|
|
||||||
~GenericMemento() noexcept {
|
|
||||||
tag_ = nullptr;
|
|
||||||
last_ = nullptr;
|
|
||||||
assert(map_.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
T* operator->() noexcept {
|
|
||||||
return &data_;
|
|
||||||
}
|
|
||||||
const T* operator->() const noexcept {
|
|
||||||
return &data_;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Tag> Save() noexcept override {
|
|
||||||
if (tag_) return tag_;
|
|
||||||
auto [itr, emplaced] = map_.emplace(next_++, data_);
|
|
||||||
assert(emplaced);
|
|
||||||
return last_ = tag_ = std::make_shared<CustomTag>(*this, itr->first);
|
|
||||||
}
|
|
||||||
void Restore(const std::shared_ptr<Tag>& tag) override {
|
|
||||||
assert(tag);
|
|
||||||
auto itr = map_.find(tag->id());
|
|
||||||
assert(itr != map_.end());
|
|
||||||
data_ = itr->second;
|
|
||||||
tag_ = tag;
|
|
||||||
last_ = tag;
|
|
||||||
onRestore();
|
|
||||||
if (file_) file_->Touch();
|
|
||||||
}
|
|
||||||
void Commit() noexcept override {
|
|
||||||
tag_ = nullptr;
|
|
||||||
onCommit();
|
|
||||||
if (file_) file_->Touch();
|
|
||||||
}
|
|
||||||
void CommitAmend() noexcept override {
|
|
||||||
if (!tag_) return;
|
|
||||||
auto itr = map_.find(tag_->id());
|
|
||||||
assert(itr != map_.end());
|
|
||||||
itr->second = data_;
|
|
||||||
onCommit();
|
|
||||||
if (file_) file_->Touch();
|
|
||||||
}
|
|
||||||
|
|
||||||
T& data() noexcept { return data_; }
|
|
||||||
const T& data() const noexcept { return data_; }
|
|
||||||
|
|
||||||
const T& last() const noexcept {
|
|
||||||
if (!last_) return initial_;
|
|
||||||
|
|
||||||
auto itr = map_.find(last_->id());
|
|
||||||
assert(itr != map_.end());
|
|
||||||
return itr->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::function<void()> onRestore = [](){};
|
|
||||||
std::function<void()> onCommit = [](){};
|
|
||||||
|
|
||||||
private:
|
|
||||||
nf7::File* const file_;
|
|
||||||
|
|
||||||
const T initial_;
|
|
||||||
T data_;
|
|
||||||
|
|
||||||
Tag::Id next_ = 0;
|
|
||||||
std::unordered_map<Tag::Id, T> map_;
|
|
||||||
|
|
||||||
std::shared_ptr<nf7::Memento::Tag> tag_;
|
|
||||||
std::shared_ptr<nf7::Memento::Tag> last_;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class GenericMemento<T>::CustomTag final : public Tag {
|
|
||||||
public:
|
|
||||||
CustomTag(GenericMemento& owner, Id id) noexcept : Tag(id), owner_(&owner) {
|
|
||||||
}
|
|
||||||
~CustomTag() noexcept {
|
|
||||||
owner_->map_.erase(id());
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
GenericMemento* owner_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,67 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <exception>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include <imgui.h>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept GenericTypeInfo_UpdateTooltip_ = requires() { T::UpdateTypeTooltip(); };
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept GenericTypeInfo_Description_ = requires() { T::kTypeDescription; };
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class GenericTypeInfo : public nf7::File::TypeInfo {
|
|
||||||
public:
|
|
||||||
GenericTypeInfo(const std::string& name, std::unordered_set<std::string>&& v) noexcept :
|
|
||||||
TypeInfo(name, AddFlags(std::move(v))) {
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<nf7::File> Deserialize(nf7::Deserializer& ar) const override
|
|
||||||
try {
|
|
||||||
return std::make_unique<T>(ar);
|
|
||||||
} catch (nf7::Exception&) {
|
|
||||||
throw nf7::DeserializeException {"deserialization failed"};
|
|
||||||
} catch (std::exception&) {
|
|
||||||
throw nf7::DeserializeException {"deserialization failed"};
|
|
||||||
}
|
|
||||||
std::unique_ptr<File> Create(nf7::Env& env) const override {
|
|
||||||
if constexpr (std::is_constructible<T, nf7::Env&>::value) {
|
|
||||||
return std::make_unique<T>(env);
|
|
||||||
} else {
|
|
||||||
throw nf7::Exception {name()+" has no factory without parameters"};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateTooltip() const noexcept override {
|
|
||||||
if constexpr (nf7::GenericTypeInfo_UpdateTooltip_<T>) {
|
|
||||||
T::UpdateTypeTooltip();
|
|
||||||
} else if constexpr (nf7::GenericTypeInfo_Description_<T>) {
|
|
||||||
ImGui::TextUnformatted(T::kTypeDescription);
|
|
||||||
} else {
|
|
||||||
ImGui::TextUnformatted("(no description)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static std::unordered_set<std::string> AddFlags(
|
|
||||||
std::unordered_set<std::string>&& flags) noexcept {
|
|
||||||
if (std::is_constructible<T, nf7::Env&>::value) {
|
|
||||||
flags.insert("nf7::File::TypeInfo::Factory");
|
|
||||||
}
|
|
||||||
return flags;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,30 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class GenericWatcher final : public Env::Watcher {
|
|
||||||
public:
|
|
||||||
using Handler = std::function<void(const File::Event&)>;
|
|
||||||
|
|
||||||
GenericWatcher(Env& env) noexcept : Watcher(env) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddHandler(File::Event::Type type, Handler&& h) noexcept {
|
|
||||||
handlers_[type] = std::move(h);
|
|
||||||
}
|
|
||||||
void Handle(const File::Event& ev) noexcept override {
|
|
||||||
auto handler = handlers_.find(ev.type);
|
|
||||||
if (handler == handlers_.end()) return;
|
|
||||||
handler->second(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unordered_map<File::Event::Type, Handler> handlers_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,59 +0,0 @@
|
|||||||
#include <string>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
#include <imgui.h>
|
|
||||||
#include <imgui_stdlib.h>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
#include "common/generic_memento.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::gui {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept ConfigData = requires (T& x) {
|
|
||||||
{ x.Stringify() } -> std::convertible_to<std::string>;
|
|
||||||
x.Parse(std::string {});
|
|
||||||
};
|
|
||||||
|
|
||||||
template <ConfigData T>
|
|
||||||
void Config(nf7::GenericMemento<T>& mem) noexcept {
|
|
||||||
static std::string text_;
|
|
||||||
static std::string msg_;
|
|
||||||
static bool mod_;
|
|
||||||
|
|
||||||
if (ImGui::IsWindowAppearing()) {
|
|
||||||
text_ = mem->Stringify();
|
|
||||||
msg_ = "";
|
|
||||||
mod_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mod_ |= ImGui::InputTextMultiline("##config", &text_);
|
|
||||||
|
|
||||||
ImGui::BeginDisabled(!mod_);
|
|
||||||
if (ImGui::Button("apply")) {
|
|
||||||
try {
|
|
||||||
mem->Parse(text_);
|
|
||||||
mem.Commit();
|
|
||||||
msg_ = "";
|
|
||||||
mod_ = false;
|
|
||||||
} catch (nf7::Exception& e) {
|
|
||||||
msg_ = e.msg();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::EndDisabled();
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button("restore")) {
|
|
||||||
text_ = mem->Stringify();
|
|
||||||
msg_ = "";
|
|
||||||
mod_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg_.size()) {
|
|
||||||
ImGui::Bullet();
|
|
||||||
ImGui::TextUnformatted(msg_.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nf7::gui
|
|
@ -1,46 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cinttypes>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <imgui.h>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::gui {
|
|
||||||
|
|
||||||
inline std::string GetContextDisplayName(const nf7::Context& ctx) noexcept {
|
|
||||||
auto f = ctx.env().GetFile(ctx.initiator());
|
|
||||||
|
|
||||||
const auto initiator =
|
|
||||||
f? f->abspath().Stringify(): std::string {"<owner missing>"};
|
|
||||||
|
|
||||||
char buf[32];
|
|
||||||
std::snprintf(buf, sizeof(buf), "(0x%0" PRIXPTR ")", reinterpret_cast<uintptr_t>(&ctx));
|
|
||||||
|
|
||||||
return initiator + " " + buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::string GetParentContextDisplayName(const nf7::Context& ctx) noexcept {
|
|
||||||
if (auto parent = ctx.parent()) {
|
|
||||||
return nf7::gui::GetContextDisplayName(*parent);
|
|
||||||
} else if (ctx.depth() == 0) {
|
|
||||||
return "(isolated)";
|
|
||||||
} else {
|
|
||||||
return "<owner disappeared> MEMORY LEAK? ;(";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void ContextStack(const nf7::Context& ctx) noexcept {
|
|
||||||
for (auto p = ctx.parent(); p; p = p->parent()) {
|
|
||||||
auto f = ctx.env().GetFile(p->initiator());
|
|
||||||
|
|
||||||
const auto path = f? f->abspath().Stringify(): "[missing file]";
|
|
||||||
|
|
||||||
ImGui::TextUnformatted(path.c_str());
|
|
||||||
ImGui::TextDisabled("%s", p->GetDescription().c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nf7::gui
|
|
@ -1,83 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
#include <imgui.h>
|
|
||||||
#include <imgui_internal.h>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::gui::dnd {
|
|
||||||
|
|
||||||
// data entity is char[] of file path
|
|
||||||
constexpr const char* kFilePath = "nf7::File::Path";
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool Send(const char* type, const T&) noexcept;
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline bool Send<std::string>(const char* type, const std::string& v) noexcept {
|
|
||||||
return ImGui::SetDragDropPayload(type, v.data(), v.size());
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
inline bool Send<std::string_view>(const char* type, const std::string_view& v) noexcept {
|
|
||||||
return ImGui::SetDragDropPayload(type, v.data(), v.size());
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
inline bool Send<File::Path>(const char* type, const File::Path& p) noexcept {
|
|
||||||
return Send(type, p.Stringify());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T To(const ImGuiPayload&) noexcept;
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline std::string To<std::string>(const ImGuiPayload& pay) noexcept {
|
|
||||||
std::string ret;
|
|
||||||
ret.resize(static_cast<size_t>(pay.DataSize));
|
|
||||||
std::memcpy(ret.data(), pay.Data, ret.size());
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
inline File::Path To<File::Path>(const ImGuiPayload& pay) noexcept {
|
|
||||||
return File::Path::Parse(To<std::string>(pay));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
std::optional<T> Accept(const char* type, ImGuiDragDropFlags flags = 0) noexcept {
|
|
||||||
if (auto pay = ImGui::AcceptDragDropPayload(type, flags)) {
|
|
||||||
return To<T>(*pay);
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
const ImGuiPayload* Peek(const char* type, auto& v, ImGuiDragDropFlags flags = 0) noexcept {
|
|
||||||
flags |= ImGuiDragDropFlags_AcceptPeekOnly;
|
|
||||||
if (auto pay = ImGui::AcceptDragDropPayload(type, flags)) {
|
|
||||||
v = To<T>(*pay);
|
|
||||||
return pay;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool IsFirstAccept() noexcept {
|
|
||||||
const auto ctx = ImGui::GetCurrentContext();
|
|
||||||
return ctx->DragDropAcceptFrameCount < ctx->FrameCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void DrawRect() noexcept {
|
|
||||||
auto& r = ImGui::GetCurrentContext()->DragDropTargetRect;
|
|
||||||
ImGui::GetForegroundDrawList()->AddRect(
|
|
||||||
r.Min - ImVec2 {3.5f, 3.5f},
|
|
||||||
r.Max + ImVec2 {3.5f, 3.5f},
|
|
||||||
ImGui::GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nf7::gui::dnd
|
|
@ -1,252 +0,0 @@
|
|||||||
#include "common/gui_file.hh"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#include <imgui.h>
|
|
||||||
#include <imgui_stdlib.h>
|
|
||||||
|
|
||||||
#include "common/dir_item.hh"
|
|
||||||
#include "common/generic_context.hh"
|
|
||||||
|
|
||||||
|
|
||||||
using namespace std::literals;
|
|
||||||
|
|
||||||
namespace nf7::gui {
|
|
||||||
|
|
||||||
static nf7::DirItem* GetDirItem(nf7::FileHolder& h, nf7::DirItem::Flags f) noexcept
|
|
||||||
try {
|
|
||||||
auto& d = h.GetFileOrThrow().interfaceOrThrow<nf7::DirItem>();
|
|
||||||
return d.flags() & f? &d: nullptr;
|
|
||||||
} catch (nf7::Exception&) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool FileFactory::Update() noexcept {
|
|
||||||
const auto em = ImGui::GetFontSize();
|
|
||||||
|
|
||||||
ImGui::PushItemWidth(16*em);
|
|
||||||
if (ImGui::IsWindowAppearing()) {
|
|
||||||
name_ = "new_file";
|
|
||||||
type_filter_ = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool submit = false;
|
|
||||||
ImGui::InputTextWithHint("##type_filter", "search...", &type_filter_);
|
|
||||||
if (ImGui::BeginListBox("type", {16*em, 8*em})) {
|
|
||||||
for (const auto& reg : nf7::File::registry()) {
|
|
||||||
const auto& t = *reg.second;
|
|
||||||
|
|
||||||
const bool match =
|
|
||||||
t.flags().contains("nf7::File::TypeInfo::Factory") &&
|
|
||||||
(type_filter_.empty() ||
|
|
||||||
t.name().find(type_filter_) != std::string::npos) &&
|
|
||||||
filter_(t);
|
|
||||||
|
|
||||||
const bool sel = (type_ == &t);
|
|
||||||
if (!match) {
|
|
||||||
if (sel) type_ = nullptr;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr auto kSelectableFlags =
|
|
||||||
ImGuiSelectableFlags_SpanAllColumns |
|
|
||||||
ImGuiSelectableFlags_AllowItemOverlap;
|
|
||||||
if (ImGui::Selectable(t.name().c_str(), sel, kSelectableFlags)) {
|
|
||||||
type_ = &t;
|
|
||||||
}
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::BeginTooltip();
|
|
||||||
t.UpdateTooltip();
|
|
||||||
ImGui::EndTooltip();
|
|
||||||
|
|
||||||
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
|
|
||||||
submit = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::EndListBox();
|
|
||||||
}
|
|
||||||
ImGui::PopItemWidth();
|
|
||||||
ImGui::Spacing();
|
|
||||||
|
|
||||||
if (flags_ & kNameInput) {
|
|
||||||
if (ImGui::IsWindowAppearing()) ImGui::SetKeyboardFocusHere();
|
|
||||||
ImGui::InputText("name", &name_);
|
|
||||||
ImGui::Spacing();
|
|
||||||
}
|
|
||||||
|
|
||||||
// input validation
|
|
||||||
bool err = false;
|
|
||||||
if (type_ == nullptr) {
|
|
||||||
ImGui::Bullet(); ImGui::TextUnformatted("type is not selected");
|
|
||||||
err = true;
|
|
||||||
}
|
|
||||||
if (flags_ & kNameInput) {
|
|
||||||
try {
|
|
||||||
nf7::File::Path::ValidateTerm(name_);
|
|
||||||
} catch (Exception& e) {
|
|
||||||
ImGui::Bullet(); ImGui::Text("invalid name: %s", e.msg().c_str());
|
|
||||||
err = true;
|
|
||||||
}
|
|
||||||
if (flags_ & kNameDupCheck) {
|
|
||||||
if (owner_->Find(name_)) {
|
|
||||||
ImGui::Bullet(); ImGui::Text("name duplicated");
|
|
||||||
err = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!err) {
|
|
||||||
if (ImGui::Button("ok")) {
|
|
||||||
submit = true;
|
|
||||||
}
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
const auto path = owner_->abspath().Stringify();
|
|
||||||
if (flags_ & kNameInput) {
|
|
||||||
ImGui::SetTooltip(
|
|
||||||
"create %s as '%s' on '%s'", type_->name().c_str(), name_.c_str(), path.c_str());
|
|
||||||
} else {
|
|
||||||
ImGui::SetTooltip("create %s on '%s'", type_->name().c_str(), path.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return submit && !err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::string FileHolderEditor::GetDisplayText() const noexcept {
|
|
||||||
std::string text;
|
|
||||||
if (holder_->own()) {
|
|
||||||
text = "[OWN] " + holder_->GetFile()->type().name();
|
|
||||||
} else if (holder_->ref()) {
|
|
||||||
text = "[REF] "s + holder_->path().Stringify();
|
|
||||||
} else if (holder_->empty()) {
|
|
||||||
text = "(empty)";
|
|
||||||
} else {
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileHolderEditor::Button(float w, bool small) noexcept {
|
|
||||||
ImGui::PushID(this);
|
|
||||||
ImGui::BeginGroup();
|
|
||||||
const auto text = GetDisplayText();
|
|
||||||
|
|
||||||
const bool open = small?
|
|
||||||
ImGui::SmallButton(text.c_str()):
|
|
||||||
ImGui::Button(text.c_str(), {w, 0});
|
|
||||||
if (open) {
|
|
||||||
ImGui::OpenPopup("FileHolderEmplacePopup_FromButton");
|
|
||||||
}
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::BeginTooltip();
|
|
||||||
Tooltip();
|
|
||||||
ImGui::EndTooltip();
|
|
||||||
}
|
|
||||||
ImGui::EndGroup();
|
|
||||||
|
|
||||||
UpdateEmplacePopup("FileHolderEmplacePopup_FromButton");
|
|
||||||
ImGui::PopID();
|
|
||||||
}
|
|
||||||
void FileHolderEditor::ButtonWithLabel(const char* name) noexcept {
|
|
||||||
ImGui::PushID(this);
|
|
||||||
ImGui::BeginGroup();
|
|
||||||
Button(ImGui::CalcItemWidth());
|
|
||||||
ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x);
|
|
||||||
ImGui::TextUnformatted(name);
|
|
||||||
ImGui::EndGroup();
|
|
||||||
ImGui::PopID();
|
|
||||||
}
|
|
||||||
void FileHolderEditor::Tooltip() noexcept {
|
|
||||||
ImGui::TextUnformatted(GetDisplayText().c_str());
|
|
||||||
ImGui::Indent();
|
|
||||||
if (auto a = GetDirItem(*holder_, nf7::DirItem::kTooltip)) {
|
|
||||||
a->UpdateTooltip();
|
|
||||||
}
|
|
||||||
ImGui::Unindent();
|
|
||||||
}
|
|
||||||
void FileHolderEditor::ItemWidget(const char* title) noexcept {
|
|
||||||
if (auto d = GetDirItem(*holder_, nf7::DirItem::kWidget)) {
|
|
||||||
if (ImGui::CollapsingHeader(title, ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
||||||
ImGui::PushID(d);
|
|
||||||
ImGui::Indent();
|
|
||||||
d->UpdateWidget();
|
|
||||||
ImGui::Unindent();
|
|
||||||
ImGui::PopID();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileHolderEditor::Update() noexcept {
|
|
||||||
ImGui::PushID(this);
|
|
||||||
if (std::exchange(open_emplace_, false)) {
|
|
||||||
ImGui::OpenPopup("FileHolderEmplacePopup_FromMenu");
|
|
||||||
}
|
|
||||||
UpdateEmplacePopup("FileHolderEmplacePopup_FromMenu");
|
|
||||||
ImGui::PopID();
|
|
||||||
}
|
|
||||||
void FileHolderEditor::UpdateEmplacePopup(const char* id) noexcept {
|
|
||||||
if (ImGui::BeginPopup(id)) {
|
|
||||||
if (ImGui::IsWindowAppearing()) {
|
|
||||||
if (holder_->ref()) {
|
|
||||||
type_ = kRef;
|
|
||||||
path_ = holder_->path().Stringify();
|
|
||||||
} else {
|
|
||||||
type_ = kOwn;
|
|
||||||
path_ = {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::RadioButton("own", type_ == kOwn)) { type_ = kOwn; }
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::RadioButton("ref", type_ == kRef)) { type_ = kRef; }
|
|
||||||
|
|
||||||
switch (type_) {
|
|
||||||
case kOwn:
|
|
||||||
if (factory_.Update()) {
|
|
||||||
ImGui::CloseCurrentPopup();
|
|
||||||
|
|
||||||
auto& f = holder_->owner();
|
|
||||||
f.env().ExecMain(
|
|
||||||
std::make_shared<nf7::GenericContext>(f),
|
|
||||||
[this]() {
|
|
||||||
holder_->Emplace(factory_.Create(holder_->owner().env()));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case kRef:
|
|
||||||
ImGui::InputText("path", &path_);
|
|
||||||
|
|
||||||
bool missing = false;
|
|
||||||
try {
|
|
||||||
auto path = nf7::File::Path::Parse(path_);
|
|
||||||
try {
|
|
||||||
holder_->owner().ResolveOrThrow(path);
|
|
||||||
} catch (nf7::File::NotFoundException&) {
|
|
||||||
missing = true;
|
|
||||||
}
|
|
||||||
if (ImGui::Button("apply")) {
|
|
||||||
ImGui::CloseCurrentPopup();
|
|
||||||
auto& f = holder_->owner();
|
|
||||||
f.env().ExecMain(
|
|
||||||
std::make_shared<nf7::GenericContext>(f),
|
|
||||||
[this, p = std::move(path)]() mutable {
|
|
||||||
holder_->Emplace(std::move(p));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (nf7::Exception& e) {
|
|
||||||
ImGui::Bullet(); ImGui::TextUnformatted(e.msg().c_str());
|
|
||||||
}
|
|
||||||
if (missing) {
|
|
||||||
ImGui::Bullet(); ImGui::TextUnformatted("the file is missing :(");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nf7::gui
|
|
@ -1,88 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
#include "common/file_base.hh"
|
|
||||||
#include "common/file_holder.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::gui {
|
|
||||||
|
|
||||||
class FileFactory final {
|
|
||||||
public:
|
|
||||||
enum Flag : uint8_t {
|
|
||||||
kNameInput = 1 << 0,
|
|
||||||
kNameDupCheck = 1 << 1,
|
|
||||||
};
|
|
||||||
using Flags = uint8_t;
|
|
||||||
using Filter = std::function<bool(const nf7::File::TypeInfo&)>;
|
|
||||||
|
|
||||||
FileFactory(nf7::File& owner, Filter&& filter, Flags flags = 0) noexcept :
|
|
||||||
owner_(&owner), filter_(std::move(filter)), flags_(flags) {
|
|
||||||
}
|
|
||||||
FileFactory(const FileFactory&) = delete;
|
|
||||||
FileFactory(FileFactory&&) = default;
|
|
||||||
FileFactory& operator=(const FileFactory&) = delete;
|
|
||||||
FileFactory& operator=(FileFactory&&) = delete;
|
|
||||||
|
|
||||||
bool Update() noexcept;
|
|
||||||
std::unique_ptr<nf7::File> Create(nf7::Env& env) noexcept {
|
|
||||||
return type_? type_->Create(env): nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string& name() const noexcept { return name_; }
|
|
||||||
const nf7::File::TypeInfo& type() const noexcept { return *type_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
nf7::File* const owner_;
|
|
||||||
const Filter filter_;
|
|
||||||
const Flags flags_;
|
|
||||||
|
|
||||||
std::string name_;
|
|
||||||
const nf7::File::TypeInfo* type_ = nullptr;
|
|
||||||
std::string type_filter_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class FileHolderEditor final : public nf7::FileBase::Feature {
|
|
||||||
public:
|
|
||||||
enum Type {
|
|
||||||
kOwn,
|
|
||||||
kRef,
|
|
||||||
};
|
|
||||||
|
|
||||||
FileHolderEditor(nf7::FileHolder& h, FileFactory::Filter&& filter) noexcept :
|
|
||||||
holder_(&h), factory_(h.owner(), std::move(filter)) {
|
|
||||||
}
|
|
||||||
FileHolderEditor(const FileHolderEditor&) = delete;
|
|
||||||
FileHolderEditor(FileHolderEditor&&) = default;
|
|
||||||
FileHolderEditor& operator=(const FileHolderEditor&) = delete;
|
|
||||||
FileHolderEditor& operator=(FileHolderEditor&&) = delete;
|
|
||||||
|
|
||||||
std::string GetDisplayText() const noexcept;
|
|
||||||
|
|
||||||
void Button(float w = 0, bool = false) noexcept;
|
|
||||||
void SmallButton() noexcept { Button(0, true); }
|
|
||||||
void ButtonWithLabel(const char* id) noexcept;
|
|
||||||
void Tooltip() noexcept;
|
|
||||||
void ItemWidget(const char*) noexcept;
|
|
||||||
|
|
||||||
void Update() noexcept override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
nf7::FileHolder* const holder_;
|
|
||||||
|
|
||||||
bool open_emplace_ = false;
|
|
||||||
|
|
||||||
Type type_;
|
|
||||||
FileFactory factory_;
|
|
||||||
std::string path_;
|
|
||||||
|
|
||||||
void UpdateEmplacePopup(const char*) noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7::gui
|
|
@ -1,66 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include <imgui.h>
|
|
||||||
#include <imgui_internal.h>
|
|
||||||
|
|
||||||
#include <ImNodes.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::gui {
|
|
||||||
|
|
||||||
inline void NodeSocket() noexcept {
|
|
||||||
auto win = ImGui::GetCurrentWindow();
|
|
||||||
|
|
||||||
const auto em = ImGui::GetFontSize();
|
|
||||||
const auto lh = std::max(win->DC.CurrLineSize.y, em);
|
|
||||||
const auto rad = em/2 / ImNodes::CanvasState().Zoom;
|
|
||||||
const auto sz = ImVec2(rad*2, lh);
|
|
||||||
const auto pos = ImGui::GetCursorScreenPos() + sz/2;
|
|
||||||
|
|
||||||
auto dlist = ImGui::GetWindowDrawList();
|
|
||||||
dlist->AddCircleFilled(
|
|
||||||
pos, rad, IM_COL32(100, 100, 100, 100));
|
|
||||||
dlist->AddCircleFilled(
|
|
||||||
pos, rad*.8f, IM_COL32(200, 200, 200, 200));
|
|
||||||
|
|
||||||
ImGui::Dummy(sz);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void NodeInputSockets(std::span<const std::string> names) noexcept {
|
|
||||||
ImGui::BeginGroup();
|
|
||||||
for (auto& name : names) {
|
|
||||||
if (ImNodes::BeginInputSlot(name.c_str(), 1)) {
|
|
||||||
ImGui::AlignTextToFramePadding();
|
|
||||||
nf7::gui::NodeSocket();
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::TextUnformatted(name.c_str());
|
|
||||||
ImNodes::EndSlot();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::EndGroup();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void NodeOutputSockets(std::span<const std::string> names) noexcept {
|
|
||||||
float maxw = 0;
|
|
||||||
for (auto& name : names) {
|
|
||||||
maxw = std::max(maxw, ImGui::CalcTextSize(name.c_str()).x);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::BeginGroup();
|
|
||||||
for (auto& name : names) {
|
|
||||||
const auto w = ImGui::CalcTextSize(name.c_str()).x;
|
|
||||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX()+maxw-w);
|
|
||||||
if (ImNodes::BeginOutputSlot(name.c_str(), 1)) {
|
|
||||||
ImGui::AlignTextToFramePadding();
|
|
||||||
ImGui::TextUnformatted(name.c_str());
|
|
||||||
ImGui::SameLine();
|
|
||||||
nf7::gui::NodeSocket();
|
|
||||||
ImNodes::EndSlot();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::EndGroup();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespacce nf7::gui
|
|
@ -1,48 +0,0 @@
|
|||||||
#include "common/gui_popup.hh"
|
|
||||||
|
|
||||||
#include <imgui_stdlib.h>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
#include "common/util_algorithm.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::gui {
|
|
||||||
|
|
||||||
void IOSocketListPopup::Update() noexcept {
|
|
||||||
if (Popup::Begin()) {
|
|
||||||
ImGui::InputTextMultiline("inputs", &is_);
|
|
||||||
ImGui::InputTextMultiline("outputs", &os_);
|
|
||||||
|
|
||||||
const auto iterm = nf7::util::SplitAndValidate(is_, nf7::File::Path::ValidateTerm);
|
|
||||||
const auto oterm = nf7::util::SplitAndValidate(os_, nf7::File::Path::ValidateTerm);
|
|
||||||
|
|
||||||
if (iterm) {
|
|
||||||
ImGui::Bullet();
|
|
||||||
ImGui::Text("invalid input name: %.*s", (int) iterm->size(), iterm->data());
|
|
||||||
}
|
|
||||||
if (oterm) {
|
|
||||||
ImGui::Bullet();
|
|
||||||
ImGui::Text("invalid output name: %.*s", (int) oterm->size(), oterm->data());
|
|
||||||
}
|
|
||||||
ImGui::Bullet();
|
|
||||||
ImGui::TextDisabled("duplicated names are removed automatically");
|
|
||||||
|
|
||||||
if (!iterm && !oterm && ImGui::Button("ok")) {
|
|
||||||
ImGui::CloseCurrentPopup();
|
|
||||||
|
|
||||||
std::vector<std::string> iv, ov;
|
|
||||||
|
|
||||||
nf7::util::SplitAndAppend(iv, is_);
|
|
||||||
nf7::util::Uniq(iv);
|
|
||||||
|
|
||||||
nf7::util::SplitAndAppend(ov, os_);
|
|
||||||
nf7::util::Uniq(ov);
|
|
||||||
|
|
||||||
onSubmit(std::move(iv), std::move(ov));
|
|
||||||
}
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nf7::gui
|
|
@ -1,67 +0,0 @@
|
|||||||
#include <functional>
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include <imgui.h>
|
|
||||||
|
|
||||||
#include "common/file_base.hh"
|
|
||||||
#include "common/util_string.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::gui {
|
|
||||||
|
|
||||||
class Popup {
|
|
||||||
public:
|
|
||||||
Popup(const char* name, ImGuiWindowFlags flags = 0) noexcept :
|
|
||||||
name_(name), flags_(flags) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void Open(ImGuiPopupFlags flags = 0) noexcept {
|
|
||||||
open_flags_ = flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Begin() noexcept {
|
|
||||||
if (auto flags = std::exchange(open_flags_, std::nullopt)) {
|
|
||||||
ImGui::OpenPopup(name_, *flags);
|
|
||||||
}
|
|
||||||
return ImGui::BeginPopup(name_, flags_);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* name() const noexcept { return name_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
const char* name_;
|
|
||||||
ImGuiWindowFlags flags_;
|
|
||||||
|
|
||||||
std::optional<ImGuiPopupFlags> open_flags_;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class IOSocketListPopup final :
|
|
||||||
public nf7::FileBase::Feature, private Popup {
|
|
||||||
public:
|
|
||||||
IOSocketListPopup(const char* name = "IOSocketListPopup",
|
|
||||||
ImGuiWindowFlags flags = 0) noexcept :
|
|
||||||
Popup(name, flags) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void Open(std::span<const std::string> iv,
|
|
||||||
std::span<const std::string> ov) noexcept {
|
|
||||||
is_ = "";
|
|
||||||
nf7::util::JoinAndAppend(is_, iv);
|
|
||||||
os_ = "";
|
|
||||||
nf7::util::JoinAndAppend(os_, ov);
|
|
||||||
Popup::Open();
|
|
||||||
}
|
|
||||||
void Update() noexcept override;
|
|
||||||
|
|
||||||
std::function<void(std::vector<std::string>&&, std::vector<std::string>&&)> onSubmit =
|
|
||||||
[](auto&&, auto&&){};
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string is_, os_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7::gui
|
|
@ -1,390 +0,0 @@
|
|||||||
#include "common/gui_timeline.hh"
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <cmath>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <imgui_internal.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::gui {
|
|
||||||
|
|
||||||
bool Timeline::Begin() noexcept {
|
|
||||||
assert(frame_state_ == kRoot);
|
|
||||||
layer_idx_ = 0;
|
|
||||||
layer_y_ = 0;
|
|
||||||
layer_h_ = 0;
|
|
||||||
|
|
||||||
layer_idx_first_display_ = 0;
|
|
||||||
layer_offset_y_.clear();
|
|
||||||
|
|
||||||
scroll_x_to_mouse_ = false;
|
|
||||||
scroll_y_to_mouse_ = false;
|
|
||||||
|
|
||||||
action_ = kNone;
|
|
||||||
action_target_ = nullptr;
|
|
||||||
|
|
||||||
if (!ImGui::BeginChild(id_, {0, 0}, false, ImGuiWindowFlags_NoMove)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
body_offset_ = {headerWidth(), xgridHeight()};
|
|
||||||
body_size_ = ImGui::GetContentRegionMax() - body_offset_;
|
|
||||||
scroll_size_.x = std::max(body_size_.x, GetXFromTime(len_) + 16*ImGui::GetFontSize());
|
|
||||||
|
|
||||||
constexpr auto kFlags =
|
|
||||||
ImGuiWindowFlags_NoScrollWithMouse |
|
|
||||||
ImGuiWindowFlags_NoScrollbar |
|
|
||||||
ImGuiWindowFlags_NoBackground;
|
|
||||||
ImGui::SetCursorPos({body_offset_.x, 0});
|
|
||||||
if (ImGui::BeginChild("xgrid", {body_size_.x, body_offset_.y}, false, kFlags)) {
|
|
||||||
UpdateXGrid();
|
|
||||||
}
|
|
||||||
ImGui::EndChild();
|
|
||||||
|
|
||||||
ImGui::SetCursorPos({0, body_offset_.y});
|
|
||||||
if (ImGui::BeginChild("layers", {0, 0}, false, kFlags)) {
|
|
||||||
frame_state_ = kHeader;
|
|
||||||
|
|
||||||
ImGui::BeginGroup();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
ImGui::EndChild();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
void Timeline::End() noexcept {
|
|
||||||
assert(frame_state_ == kRoot);
|
|
||||||
ImGui::EndChild(); // end of root
|
|
||||||
}
|
|
||||||
|
|
||||||
void Timeline::NextLayerHeader(Layer layer, float height) noexcept {
|
|
||||||
assert(frame_state_ == kHeader);
|
|
||||||
assert(height > 0);
|
|
||||||
|
|
||||||
const auto em = ImGui::GetFontSize();
|
|
||||||
|
|
||||||
if (layer_h_ > 0) {
|
|
||||||
++layer_idx_;
|
|
||||||
layer_y_ += layer_h_+padding()*2;
|
|
||||||
}
|
|
||||||
layer_h_ = height*em;
|
|
||||||
layer_ = layer;
|
|
||||||
|
|
||||||
// save Y offset of the layer if shown
|
|
||||||
if (layer_idx_first_display_) {
|
|
||||||
if (layer_y_ < scroll_.y+body_size_.y) {
|
|
||||||
layer_offset_y_.push_back(layer_y_);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const auto bottom = layer_y_+layer_h_;
|
|
||||||
if (bottom > scroll_.y) {
|
|
||||||
layer_idx_first_display_ = layer_idx_;
|
|
||||||
layer_offset_y_.push_back(layer_y_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto mouse = ImGui::GetMousePos().y;
|
|
||||||
if (layerTopScreenY() <= mouse && mouse < layerBottomScreenY()) {
|
|
||||||
mouse_layer_ = layer;
|
|
||||||
mouse_layer_y_ = layer_y_;
|
|
||||||
mouse_layer_h_ = layer_h_;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SetCursorPos({0, std::round(layer_y_)});
|
|
||||||
const auto col = ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.5f);
|
|
||||||
const auto spos = ImGui::GetCursorScreenPos();
|
|
||||||
const auto size = ImGui::GetWindowSize();
|
|
||||||
|
|
||||||
auto d = ImGui::GetWindowDrawList();
|
|
||||||
d->AddLine({spos.x, spos.y}, {spos.x+size.x, spos.y}, col);
|
|
||||||
|
|
||||||
ImGui::SetCursorPos({0, std::round(layer_y_+padding())});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Timeline::BeginBody() noexcept {
|
|
||||||
assert(frame_state_ == kHeader);
|
|
||||||
|
|
||||||
const auto em = ImGui::GetFontSize();
|
|
||||||
const auto ctx = ImGui::GetCurrentContext();
|
|
||||||
const auto& io = ImGui::GetIO();
|
|
||||||
|
|
||||||
// end of header group
|
|
||||||
ImGui::EndGroup();
|
|
||||||
scroll_size_.y = ImGui::GetItemRectSize().y;
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
if (auto wh = ImGui::GetIO().MouseWheel) {
|
|
||||||
scroll_.y -= wh * 5*em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// beginnign of body
|
|
||||||
constexpr auto kFlags = ImGuiWindowFlags_NoBackground;
|
|
||||||
ImGui::SameLine(0, 0);
|
|
||||||
if (ImGui::BeginChild("body", {0, scroll_size_.y}, false, kFlags)) {
|
|
||||||
frame_state_ = kBody;
|
|
||||||
body_screen_offset_ = ImGui::GetCursorScreenPos();
|
|
||||||
|
|
||||||
ImGui::InvisibleButton(
|
|
||||||
"viewport-grip", scroll_size_,
|
|
||||||
ImGuiButtonFlags_MouseButtonMiddle |
|
|
||||||
ImGuiButtonFlags_MouseButtonLeft);
|
|
||||||
ImGui::SetItemAllowOverlap();
|
|
||||||
if (ImGui::IsItemActive()) {
|
|
||||||
switch (ctx->ActiveIdMouseButton) {
|
|
||||||
case ImGuiMouseButton_Left: // click timeline to set time
|
|
||||||
action_time_ = GetTimeFromScreenX(io.MousePos.x);
|
|
||||||
if (ImGui::IsItemActivated() || action_time_ != action_last_set_time_) {
|
|
||||||
action_ = kSetTime;
|
|
||||||
action_last_set_time_ = action_time_;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ImGuiMouseButton_Middle: // easyscroll
|
|
||||||
scroll_ -= io.MouseDelta;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
len_ = 0;
|
|
||||||
layer_ = nullptr;
|
|
||||||
layer_idx_ = 0;
|
|
||||||
layer_y_ = 0;
|
|
||||||
layer_h_ = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
void Timeline::EndBody() noexcept {
|
|
||||||
assert(frame_state_ == kBody);
|
|
||||||
frame_state_ = kRoot;
|
|
||||||
|
|
||||||
const auto& io = ImGui::GetIO();
|
|
||||||
const auto em = ImGui::GetFontSize();
|
|
||||||
|
|
||||||
// manipulation by mouse
|
|
||||||
if (ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows)) {
|
|
||||||
if (io.MouseWheel) {
|
|
||||||
if (io.KeyCtrl) {
|
|
||||||
const auto xscroll_base = scroll_.x/zoom_;
|
|
||||||
|
|
||||||
// zoom
|
|
||||||
const auto zmin = 16.f / static_cast<float>(len_);
|
|
||||||
zoom_ += (zoom_*.99f+.01f)*.1f*io.MouseWheel;
|
|
||||||
zoom_ = std::clamp(zoom_, zmin, 1.f);
|
|
||||||
|
|
||||||
scroll_.x = xscroll_base * zoom_;
|
|
||||||
} else {
|
|
||||||
// x-scrolling
|
|
||||||
scroll_.x -= io.MouseWheel * 2*em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// move x scroll to the mouse
|
|
||||||
if (scroll_x_to_mouse_) {
|
|
||||||
const auto x = ImGui::GetMousePos().x-body_screen_offset_.x;
|
|
||||||
if (x < scroll_.x+2*em) {
|
|
||||||
scroll_.x = x-2*em;
|
|
||||||
} else {
|
|
||||||
const auto right = scroll_.x+body_size_.x - 2*em;
|
|
||||||
if (x > right) {
|
|
||||||
scroll_.x += x-right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scroll_.x = std::clamp(scroll_.x, 0.f, std::max(0.f, scroll_size_.x-body_size_.x));
|
|
||||||
ImGui::SetScrollX(scroll_.x);
|
|
||||||
ImGui::EndChild();
|
|
||||||
|
|
||||||
// move y scroll to the mouse
|
|
||||||
if (scroll_y_to_mouse_ && mouse_layer_) {
|
|
||||||
if (mouse_layer_y_ < scroll_.y) {
|
|
||||||
scroll_.y = mouse_layer_y_;
|
|
||||||
} else {
|
|
||||||
const auto bottom = mouse_layer_y_+mouse_layer_h_;
|
|
||||||
if (bottom > scroll_.y+body_size_.y) {
|
|
||||||
scroll_.y = bottom-body_size_.y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scroll_.y = std::clamp(scroll_.y, 0.f, std::max(0.f, scroll_size_.y-body_size_.y));
|
|
||||||
ImGui::SetScrollY(scroll_.y);
|
|
||||||
ImGui::EndChild(); // end of layers
|
|
||||||
}
|
|
||||||
bool Timeline::NextLayer(Layer layer, float height) noexcept {
|
|
||||||
assert(frame_state_ == kBody);
|
|
||||||
assert(height > 0);
|
|
||||||
|
|
||||||
const auto em = ImGui::GetFontSize();
|
|
||||||
|
|
||||||
if (layer_h_ > 0) {
|
|
||||||
++layer_idx_;
|
|
||||||
layer_y_ += layer_h_+padding()*2;
|
|
||||||
}
|
|
||||||
layer_h_ = height*em;
|
|
||||||
layer_ = layer;
|
|
||||||
|
|
||||||
// it's shown if y offset is saved
|
|
||||||
return !!layerTopY(layer_idx_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Timeline::BeginItem(Item item, uint64_t begin, uint64_t end) noexcept {
|
|
||||||
assert(frame_state_ == kBody);
|
|
||||||
frame_state_ = kItem;
|
|
||||||
|
|
||||||
len_ = std::max(len_, end);
|
|
||||||
item_ = item;
|
|
||||||
|
|
||||||
const auto em = ImGui::GetFontSize();
|
|
||||||
const auto pad = padding();
|
|
||||||
const auto left = GetXFromTime(begin);
|
|
||||||
const auto right = GetXFromTime(end);
|
|
||||||
|
|
||||||
const auto w = std::max(1.f, right-left);
|
|
||||||
const auto h = layer_h_;
|
|
||||||
|
|
||||||
ImGui::SetCursorPos({left, std::round(layer_y_+pad)});
|
|
||||||
|
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2 {0, 0});
|
|
||||||
constexpr auto kFlags =
|
|
||||||
ImGuiWindowFlags_NoScrollbar |
|
|
||||||
ImGuiWindowFlags_NoScrollWithMouse;
|
|
||||||
const bool shown = ImGui::BeginChild(ImGui::GetID(item), {w, h}, true, kFlags);
|
|
||||||
ImGui::PopStyleVar(1);
|
|
||||||
|
|
||||||
if (shown) {
|
|
||||||
const auto resizer_w = std::min(1*em, w/2);
|
|
||||||
|
|
||||||
ImGui::SetCursorPos({0, 0});
|
|
||||||
ImGui::InvisibleButton("begin", {resizer_w, h});
|
|
||||||
ImGui::SetItemAllowOverlap();
|
|
||||||
HandleGrip(item, 0, kResizeBegin, kResizeBeginDone, ImGuiMouseCursor_ResizeEW);
|
|
||||||
|
|
||||||
ImGui::SetCursorPos({w-resizer_w, 0});
|
|
||||||
ImGui::InvisibleButton("end", {resizer_w, h});
|
|
||||||
ImGui::SetItemAllowOverlap();
|
|
||||||
HandleGrip(item, -resizer_w, kResizeEnd, kResizeEndDone, ImGuiMouseCursor_ResizeEW);
|
|
||||||
|
|
||||||
const auto mover_w = std::max(1.f, w-resizer_w*2);
|
|
||||||
ImGui::SetCursorPos({resizer_w, 0});
|
|
||||||
ImGui::InvisibleButton("mover", {mover_w, h});
|
|
||||||
ImGui::SetItemAllowOverlap();
|
|
||||||
HandleGrip(item, resizer_w, kMove, kMoveDone, ImGuiMouseCursor_Hand);
|
|
||||||
|
|
||||||
const auto wpad = ImGui::GetStyle().WindowPadding / 2;
|
|
||||||
ImGui::SetCursorPosY(wpad.y);
|
|
||||||
ImGui::Indent(wpad.x);
|
|
||||||
}
|
|
||||||
return shown;
|
|
||||||
}
|
|
||||||
void Timeline::EndItem() noexcept {
|
|
||||||
assert(frame_state_ == kItem);
|
|
||||||
frame_state_ = kBody;
|
|
||||||
ImGui::Unindent();
|
|
||||||
ImGui::EndChild();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Timeline::Cursor(const char* name, uint64_t t, uint32_t col) noexcept {
|
|
||||||
const auto d = ImGui::GetWindowDrawList();
|
|
||||||
|
|
||||||
const auto spos = ImGui::GetWindowPos();
|
|
||||||
const auto size = ImGui::GetWindowSize();
|
|
||||||
const auto grid_h = xgridHeight();
|
|
||||||
const auto x = GetScreenXFromTime(t);
|
|
||||||
if (x < body_screen_offset_.x || x > body_screen_offset_.x+body_size_.x) return;
|
|
||||||
|
|
||||||
d->AddLine({x, spos.y}, {x, spos.y+size.y}, col);
|
|
||||||
|
|
||||||
const auto em = ImGui::GetFontSize();
|
|
||||||
const auto num = std::to_string(t);
|
|
||||||
d->AddText({x, spos.y + grid_h*0.1f }, col, num.c_str());
|
|
||||||
d->AddText({x, spos.y + grid_h*0.1f+em}, col, name);
|
|
||||||
}
|
|
||||||
void Timeline::Arrow(uint64_t t, uint64_t layer, uint32_t col) noexcept {
|
|
||||||
const auto d = ImGui::GetWindowDrawList();
|
|
||||||
|
|
||||||
const auto em = ImGui::GetFontSize();
|
|
||||||
|
|
||||||
const auto x = GetScreenXFromTime(t);
|
|
||||||
if (x < body_offset_.x || x > body_offset_.x+body_size_.x) return;
|
|
||||||
|
|
||||||
const auto y = layerTopScreenY(layer);
|
|
||||||
if (!y || *y < scroll_.y) return;
|
|
||||||
|
|
||||||
d->AddTriangleFilled({x, *y}, {x+em, *y-em/2}, {x+em, *y+em/2}, col);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Timeline::UpdateXGrid() noexcept {
|
|
||||||
constexpr uint64_t kAccentInterval = 5;
|
|
||||||
|
|
||||||
const uint64_t unit_min = static_cast<uint64_t>(1/zoom_);
|
|
||||||
uint64_t unit = 1;
|
|
||||||
while (unit < unit_min) unit *= 10;
|
|
||||||
|
|
||||||
const auto spos = ImGui::GetWindowPos();
|
|
||||||
const auto size = ImGui::GetContentRegionMax();
|
|
||||||
const auto color = ImGui::GetColorU32(ImGuiCol_TextDisabled);
|
|
||||||
const auto left = GetTimeFromX(scroll_.x)/unit*unit;
|
|
||||||
const auto right = GetTimeFromX(scroll_.x+body_size_.x)+1;
|
|
||||||
|
|
||||||
const auto d = ImGui::GetWindowDrawList();
|
|
||||||
|
|
||||||
for (uint64_t t = left; t < right; t += unit) {
|
|
||||||
const bool accent = !((t/unit)%kAccentInterval);
|
|
||||||
|
|
||||||
const auto x = GetScreenXFromTime(t);
|
|
||||||
const auto y = spos.y + size.y;
|
|
||||||
const auto h = accent? size.y*0.2f: size.y*0.1f;
|
|
||||||
d->AddLine({x, y}, {x, y-h}, color);
|
|
||||||
|
|
||||||
if (accent) {
|
|
||||||
const auto num = std::to_string(t);
|
|
||||||
const auto num_size = ImGui::CalcTextSize(num.c_str());
|
|
||||||
d->AddText({x - num_size.x/2, y-h - num_size.y}, color, num.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Timeline::HandleGrip(Item item, float off, Action ac, Action acdone, ImGuiMouseCursor cur) noexcept {
|
|
||||||
auto ctx = ImGui::GetCurrentContext();
|
|
||||||
auto& io = ImGui::GetIO();
|
|
||||||
|
|
||||||
if (ImGui::IsItemActive()) {
|
|
||||||
if (ImGui::IsItemActivated()) {
|
|
||||||
action_grip_moved_ = false;
|
|
||||||
} else {
|
|
||||||
action_ = ac;
|
|
||||||
if (io.MouseDelta.x != 0 || io.MouseDelta.y != 0) {
|
|
||||||
action_grip_moved_ = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
action_target_ = item;
|
|
||||||
ImGui::SetMouseCursor(cur);
|
|
||||||
|
|
||||||
off -= 1;
|
|
||||||
off += ctx->ActiveIdClickOffset.x;
|
|
||||||
|
|
||||||
const auto pos = ImGui::GetMousePos() - ImVec2{off, 0};
|
|
||||||
action_time_ = GetTimeFromScreenX(pos.x);
|
|
||||||
|
|
||||||
scroll_x_to_mouse_ = true;
|
|
||||||
scroll_y_to_mouse_ = (ac == kMove);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (ImGui::IsItemDeactivated()) {
|
|
||||||
action_ = action_grip_moved_? acdone: kSelect;
|
|
||||||
action_target_ = item;
|
|
||||||
}
|
|
||||||
if (ctx->LastItemData.ID == ctx->HoveredIdPreviousFrame) {
|
|
||||||
ImGui::SetMouseCursor(cur);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace nf7::gui
|
|
@ -1,206 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include <imgui.h>
|
|
||||||
|
|
||||||
#include <yas/serialize.hpp>
|
|
||||||
#include <yas/types/utility/usertype.hpp>
|
|
||||||
|
|
||||||
#include "common/yas_imgui.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::gui {
|
|
||||||
|
|
||||||
// if (tl.Begin()) {
|
|
||||||
// tl.NextLayerHeader(layer1, &layer1_height)
|
|
||||||
// ImGui::Button("layer1");
|
|
||||||
// tl.NextLayerHeader(layer2, &layer2_height)
|
|
||||||
// ImGui::Button("layer2");
|
|
||||||
//
|
|
||||||
// if (tl.BeginBody()) {
|
|
||||||
// tl.NextLayer(layer1, &layer);
|
|
||||||
// if (tl.BeginItem(layer1_item1, 0, 10)) {
|
|
||||||
// // update item
|
|
||||||
// }
|
|
||||||
// tl.EndItem();
|
|
||||||
// if (tl.BeginItem(layer1_item2, 0, 10)) {
|
|
||||||
// // update item
|
|
||||||
// }
|
|
||||||
// tl.EndItem();
|
|
||||||
//
|
|
||||||
// tl.NextLayer(layer2, &layer);
|
|
||||||
// if (tl.BeginItem(layer2_item1, 0, 10)) {
|
|
||||||
// // update item
|
|
||||||
// }
|
|
||||||
// tl.EndItem();
|
|
||||||
// if (tl.BeginItem(layer2_item2, 0, 10)) {
|
|
||||||
// // update item
|
|
||||||
// }
|
|
||||||
// tl.EndItem();
|
|
||||||
// }
|
|
||||||
// tl_.EndBody();
|
|
||||||
//
|
|
||||||
// tl_.Cursor(...);
|
|
||||||
// tl_.Cursor(...);
|
|
||||||
//
|
|
||||||
// // handle actions
|
|
||||||
// }
|
|
||||||
// tl.End();
|
|
||||||
|
|
||||||
struct Timeline {
|
|
||||||
public:
|
|
||||||
enum Action {
|
|
||||||
kNone,
|
|
||||||
kSelect,
|
|
||||||
kResizeBegin,
|
|
||||||
kResizeBeginDone,
|
|
||||||
kResizeEnd,
|
|
||||||
kResizeEndDone,
|
|
||||||
kMove,
|
|
||||||
kMoveDone,
|
|
||||||
kSetTime,
|
|
||||||
};
|
|
||||||
using Layer = void*;
|
|
||||||
using Item = void*;
|
|
||||||
|
|
||||||
Timeline() = delete;
|
|
||||||
Timeline(const char* id) noexcept : id_(id) {
|
|
||||||
}
|
|
||||||
Timeline(const Timeline&) = default;
|
|
||||||
Timeline(Timeline&&) = delete;
|
|
||||||
Timeline& operator=(const Timeline&) = default;
|
|
||||||
Timeline& operator=(Timeline&&) = delete;
|
|
||||||
|
|
||||||
template <typename Ar>
|
|
||||||
void serialize(Ar& ar) {
|
|
||||||
ar(header_width_);
|
|
||||||
ar(xgrid_height_);
|
|
||||||
ar(zoom_);
|
|
||||||
ar(padding_);
|
|
||||||
ar(scroll_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Begin() noexcept;
|
|
||||||
void End() noexcept;
|
|
||||||
|
|
||||||
void NextLayerHeader(Layer layer, float height) noexcept;
|
|
||||||
|
|
||||||
bool BeginBody() noexcept;
|
|
||||||
void EndBody() noexcept;
|
|
||||||
bool NextLayer(Layer layer, float height) noexcept;
|
|
||||||
|
|
||||||
bool BeginItem(Item item, uint64_t begin, uint64_t end) noexcept;
|
|
||||||
void EndItem() noexcept;
|
|
||||||
|
|
||||||
void Cursor(const char*, uint64_t t, uint32_t col) noexcept;
|
|
||||||
void Arrow(uint64_t t, uint64_t layer, uint32_t col) noexcept;
|
|
||||||
|
|
||||||
uint64_t GetTimeFromX(float x) const noexcept {
|
|
||||||
return static_cast<uint64_t>(std::max(0.f, x/ImGui::GetFontSize()/zoom_));
|
|
||||||
}
|
|
||||||
uint64_t GetTimeFromScreenX(float x) const noexcept {
|
|
||||||
return GetTimeFromX(x - body_screen_offset_.x);
|
|
||||||
}
|
|
||||||
float GetXFromTime(uint64_t t) const noexcept {
|
|
||||||
return static_cast<float>(t)*zoom_*ImGui::GetFontSize();
|
|
||||||
}
|
|
||||||
float GetScreenXFromTime(uint64_t t) const noexcept {
|
|
||||||
return GetXFromTime(t)+body_screen_offset_.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
float zoom() const noexcept { return zoom_; }
|
|
||||||
float headerWidth() const noexcept { return header_width_*ImGui::GetFontSize(); }
|
|
||||||
float xgridHeight() const noexcept { return xgrid_height_*ImGui::GetFontSize(); }
|
|
||||||
float padding() const noexcept { return padding_*ImGui::GetFontSize(); }
|
|
||||||
|
|
||||||
std::optional<float> layerTopY(size_t idx) noexcept {
|
|
||||||
if (!layer_idx_first_display_ || idx < *layer_idx_first_display_) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
idx -= *layer_idx_first_display_;
|
|
||||||
if (idx >= layer_offset_y_.size()) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
return layer_offset_y_[idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<float> layerTopScreenY(size_t idx) noexcept {
|
|
||||||
auto y = layerTopY(idx);
|
|
||||||
if (!y) return std::nullopt;
|
|
||||||
return *y + body_screen_offset_.y;
|
|
||||||
}
|
|
||||||
float layerTopScreenY() noexcept {
|
|
||||||
return body_screen_offset_.y + layer_y_;
|
|
||||||
}
|
|
||||||
float layerBottomScreenY() noexcept {
|
|
||||||
return layerTopScreenY() + layerH() + padding()*2;
|
|
||||||
}
|
|
||||||
float layerH() noexcept {
|
|
||||||
return layer_h_;
|
|
||||||
}
|
|
||||||
|
|
||||||
Layer mouseLayer() const noexcept { return mouse_layer_; }
|
|
||||||
uint64_t mouseTime() const noexcept {
|
|
||||||
return GetTimeFromScreenX(ImGui::GetMousePos().x);
|
|
||||||
}
|
|
||||||
|
|
||||||
Action action() const noexcept { return action_; }
|
|
||||||
Item actionTarget() const noexcept { return action_target_; }
|
|
||||||
uint64_t actionTime() const noexcept { return action_time_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
// immutable params
|
|
||||||
const char* id_;
|
|
||||||
|
|
||||||
// permanentized params
|
|
||||||
float header_width_ = 4.f;
|
|
||||||
float xgrid_height_ = 4.f;
|
|
||||||
float zoom_ = 1.f;
|
|
||||||
float padding_ = 0.2f;
|
|
||||||
ImVec2 scroll_;
|
|
||||||
|
|
||||||
// temporary values (immutable on each frame)
|
|
||||||
ImVec2 body_size_;
|
|
||||||
ImVec2 body_offset_;
|
|
||||||
ImVec2 body_screen_offset_;
|
|
||||||
|
|
||||||
// volatile params
|
|
||||||
enum {kRoot, kHeader, kBody, kItem} frame_state_ = kRoot;
|
|
||||||
|
|
||||||
uint64_t len_ = 0;
|
|
||||||
ImVec2 scroll_size_;
|
|
||||||
bool scroll_x_to_mouse_;
|
|
||||||
bool scroll_y_to_mouse_;
|
|
||||||
|
|
||||||
|
|
||||||
Layer mouse_layer_;
|
|
||||||
float mouse_layer_y_;
|
|
||||||
float mouse_layer_h_;
|
|
||||||
|
|
||||||
Layer layer_;
|
|
||||||
size_t layer_idx_;
|
|
||||||
float layer_y_;
|
|
||||||
float layer_h_;
|
|
||||||
|
|
||||||
std::optional<size_t> layer_idx_first_display_;
|
|
||||||
std::vector<float> layer_offset_y_;
|
|
||||||
|
|
||||||
Item item_;
|
|
||||||
|
|
||||||
Action action_;
|
|
||||||
Item action_target_;
|
|
||||||
uint64_t action_time_;
|
|
||||||
bool action_grip_moved_;
|
|
||||||
|
|
||||||
uint64_t action_last_set_time_ = UINT64_MAX; // for kSetTime
|
|
||||||
|
|
||||||
|
|
||||||
void UpdateXGrid() noexcept;
|
|
||||||
void HandleGrip(
|
|
||||||
Item item, float off, Action ac, Action acdone, ImGuiMouseCursor cur) noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7::gui
|
|
@ -1,115 +0,0 @@
|
|||||||
#include "common/gui_value.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::gui {
|
|
||||||
|
|
||||||
bool Value::ReplaceType(Type t) noexcept {
|
|
||||||
if (type_ == t) return false;
|
|
||||||
|
|
||||||
type_ = t;
|
|
||||||
switch (type_) {
|
|
||||||
case nf7::gui::Value::kPulse:
|
|
||||||
entity_ = nf7::Value::Pulse {};
|
|
||||||
break;
|
|
||||||
case nf7::gui::Value::kInteger:
|
|
||||||
entity_ = nf7::Value::Integer {0};
|
|
||||||
break;
|
|
||||||
case nf7::gui::Value::kScalar:
|
|
||||||
case nf7::gui::Value::kNormalizedScalar:
|
|
||||||
entity_ = nf7::Value::Scalar {0};
|
|
||||||
break;
|
|
||||||
case nf7::gui::Value::kString:
|
|
||||||
case nf7::gui::Value::kMultilineString:
|
|
||||||
entity_ = nf7::Value::String {};
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Value::ValidateValue() const {
|
|
||||||
bool valid = true;
|
|
||||||
switch (type_) {
|
|
||||||
case nf7::gui::Value::kPulse:
|
|
||||||
valid = entity_.isPulse();
|
|
||||||
break;
|
|
||||||
case nf7::gui::Value::kInteger:
|
|
||||||
valid = entity_.isInteger();
|
|
||||||
break;
|
|
||||||
case nf7::gui::Value::kScalar:
|
|
||||||
case nf7::gui::Value::kNormalizedScalar:
|
|
||||||
valid = entity_.isScalar();
|
|
||||||
break;
|
|
||||||
case nf7::gui::Value::kString:
|
|
||||||
case nf7::gui::Value::kMultilineString:
|
|
||||||
valid = entity_.isString();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!valid) {
|
|
||||||
throw nf7::DeserializeException {"invalid entity type"};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Value::UpdateTypeButton(const char* name, bool small) noexcept {
|
|
||||||
if (name == nullptr) {
|
|
||||||
name = StringifyShortType(type_);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (small) {
|
|
||||||
ImGui::SmallButton(name);
|
|
||||||
} else {
|
|
||||||
ImGui::Button(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ret = false;
|
|
||||||
if (ImGui::BeginPopupContextItem(nullptr, ImGuiPopupFlags_MouseButtonLeft)) {
|
|
||||||
for (const auto t : kTypes) {
|
|
||||||
if (ImGui::MenuItem(StringifyType(t), nullptr, type_ == t)) {
|
|
||||||
ret |= ReplaceType(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Value::UpdateEditor() noexcept {
|
|
||||||
bool ret = false;
|
|
||||||
const auto w = ImGui::CalcItemWidth();
|
|
||||||
|
|
||||||
const auto em = ImGui::GetFontSize();
|
|
||||||
switch (type_) {
|
|
||||||
case kPulse:
|
|
||||||
ImGui::BeginDisabled();
|
|
||||||
ImGui::Button("PULSE", {w, 0});
|
|
||||||
ImGui::EndDisabled();
|
|
||||||
break;
|
|
||||||
case kInteger:
|
|
||||||
ImGui::DragScalar("##value", ImGuiDataType_S64, &entity_.integer());
|
|
||||||
ret |= ImGui::IsItemDeactivatedAfterEdit();
|
|
||||||
break;
|
|
||||||
case kScalar:
|
|
||||||
ImGui::DragScalar("##value", ImGuiDataType_Double, &entity_.scalar());
|
|
||||||
ret |= ImGui::IsItemDeactivatedAfterEdit();
|
|
||||||
break;
|
|
||||||
case kNormalizedScalar:
|
|
||||||
ImGui::DragScalar("##value", ImGuiDataType_Double, &entity_.scalar());
|
|
||||||
ret |= ImGui::IsItemDeactivatedAfterEdit();
|
|
||||||
break;
|
|
||||||
case kString:
|
|
||||||
ImGui::InputTextWithHint("##value", "string", &entity_.string());
|
|
||||||
ret |= ImGui::IsItemDeactivatedAfterEdit();
|
|
||||||
break;
|
|
||||||
case kMultilineString:
|
|
||||||
ImGui::InputTextMultiline("##value", &entity_.string(), {w, 2.4f*em});
|
|
||||||
ret |= ImGui::IsItemDeactivatedAfterEdit();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nf7::gui
|
|
@ -1,122 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include <imgui.h>
|
|
||||||
#include <imgui_stdlib.h>
|
|
||||||
|
|
||||||
#include <yas/serialize.hpp>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
#include "common/value.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::gui {
|
|
||||||
|
|
||||||
class Value {
|
|
||||||
public:
|
|
||||||
enum Type {
|
|
||||||
kPulse, kInteger, kScalar, kNormalizedScalar, kString, kMultilineString,
|
|
||||||
};
|
|
||||||
static inline const Type kTypes[] = {
|
|
||||||
kPulse, kInteger, kScalar, kNormalizedScalar, kString, kMultilineString,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char* StringifyType(Type t) noexcept {
|
|
||||||
switch (t) {
|
|
||||||
case kPulse: return "Pulse";
|
|
||||||
case kInteger: return "Integer";
|
|
||||||
case kScalar: return "Scalar";
|
|
||||||
case kNormalizedScalar: return "NormalizedScalar";
|
|
||||||
case kString: return "String";
|
|
||||||
case kMultilineString: return "MultilineString";
|
|
||||||
}
|
|
||||||
assert(false);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
static const char* StringifyShortType(Type t) noexcept {
|
|
||||||
switch (t) {
|
|
||||||
case kPulse: return "Pulse";
|
|
||||||
case kInteger: return "Integer";
|
|
||||||
case kScalar: return "Scalar";
|
|
||||||
case kNormalizedScalar: return "NScalar";
|
|
||||||
case kString: return "String";
|
|
||||||
case kMultilineString: return "MString";
|
|
||||||
}
|
|
||||||
assert(false);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
static Type ParseType(std::string_view v) {
|
|
||||||
return
|
|
||||||
v == "Pulse"? kPulse:
|
|
||||||
v == "Integer"? kInteger:
|
|
||||||
v == "Scalar"? kScalar:
|
|
||||||
v == "NormalizedScalar"? kNormalizedScalar:
|
|
||||||
v == "String"? kString:
|
|
||||||
v == "MultilineString"? kMultilineString:
|
|
||||||
throw nf7::DeserializeException {"unknown type: "+std::string {v}};
|
|
||||||
}
|
|
||||||
|
|
||||||
Value() = default;
|
|
||||||
Value(const Value&) = default;
|
|
||||||
Value(Value&&) = default;
|
|
||||||
Value& operator=(const Value&) = default;
|
|
||||||
Value& operator=(Value&&) = default;
|
|
||||||
|
|
||||||
bool ReplaceType(Type t) noexcept;
|
|
||||||
|
|
||||||
void ReplaceEntity(const nf7::Value& v) {
|
|
||||||
entity_ = v;
|
|
||||||
ValidateValue();
|
|
||||||
}
|
|
||||||
void ReplaceEntity(nf7::Value&& v) {
|
|
||||||
entity_ = std::move(v);
|
|
||||||
ValidateValue();
|
|
||||||
}
|
|
||||||
void ValidateValue() const;
|
|
||||||
|
|
||||||
bool UpdateTypeButton(const char* name = nullptr, bool small = false) noexcept;
|
|
||||||
bool UpdateEditor() noexcept;
|
|
||||||
|
|
||||||
Type type() const noexcept { return type_; }
|
|
||||||
const nf7::Value& entity() const noexcept { return entity_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
Type type_ = kInteger;
|
|
||||||
nf7::Value entity_ = nf7::Value::Integer {0};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7::gui
|
|
||||||
|
|
||||||
|
|
||||||
namespace yas::detail {
|
|
||||||
|
|
||||||
template <size_t F>
|
|
||||||
struct serializer<
|
|
||||||
type_prop::not_a_fundamental,
|
|
||||||
ser_case::use_internal_serializer,
|
|
||||||
F,
|
|
||||||
nf7::gui::Value> {
|
|
||||||
public:
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& save(Archive& ar, const nf7::gui::Value& v) {
|
|
||||||
ar(std::string_view {v.StringifyType(v.type())}, v.entity());
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& load(Archive& ar, nf7::gui::Value& v) {
|
|
||||||
std::string type;
|
|
||||||
nf7::Value entity;
|
|
||||||
ar(type, entity);
|
|
||||||
|
|
||||||
v.ReplaceType(v.ParseType(type));
|
|
||||||
v.ReplaceEntity(entity);
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace yas::detail
|
|
@ -1,82 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
#include <imgui.h>
|
|
||||||
#include <yas/serialize.hpp>
|
|
||||||
#include <yas/types/utility/usertype.hpp>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::gui {
|
|
||||||
|
|
||||||
class Window {
|
|
||||||
public:
|
|
||||||
static std::string ConcatId(nf7::File& f, const std::string& name) noexcept {
|
|
||||||
return f.abspath().Stringify() + " | " + std::string {name};
|
|
||||||
}
|
|
||||||
|
|
||||||
Window() = delete;
|
|
||||||
Window(File& owner, std::string_view title, const gui::Window* src = nullptr) noexcept :
|
|
||||||
owner_(&owner), title_(title),
|
|
||||||
shown_(src? src->shown_: false) {
|
|
||||||
}
|
|
||||||
Window(const Window&) = delete;
|
|
||||||
Window(Window&&) = delete;
|
|
||||||
Window& operator=(const Window&) = delete;
|
|
||||||
Window& operator=(Window&&) = delete;
|
|
||||||
|
|
||||||
bool Begin() noexcept {
|
|
||||||
if (std::exchange(set_focus_, false)) {
|
|
||||||
ImGui::SetNextWindowFocus();
|
|
||||||
shown_ = true;
|
|
||||||
}
|
|
||||||
if (!shown_) return false;
|
|
||||||
|
|
||||||
need_end_ = true;
|
|
||||||
return ImGui::Begin(id().c_str(), &shown_);
|
|
||||||
}
|
|
||||||
void End() noexcept {
|
|
||||||
if (need_end_) {
|
|
||||||
ImGui::End();
|
|
||||||
need_end_ = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetFocus() noexcept {
|
|
||||||
shown_ = true;
|
|
||||||
set_focus_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Ar>
|
|
||||||
Ar& serialize(Ar& ar) {
|
|
||||||
ar(shown_);
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string id() const noexcept {
|
|
||||||
return ConcatId(*owner_, title_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool shownInCurrentFrame() const noexcept {
|
|
||||||
return shown_ || set_focus_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool shown() const noexcept { return shown_; }
|
|
||||||
bool& shown() noexcept { return shown_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
File* const owner_;
|
|
||||||
std::string title_;
|
|
||||||
|
|
||||||
bool need_end_ = false;
|
|
||||||
bool set_focus_ = false;
|
|
||||||
|
|
||||||
// persistent params
|
|
||||||
bool shown_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7::gui
|
|
@ -1,53 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class History {
|
|
||||||
public:
|
|
||||||
class Command;
|
|
||||||
class CorruptException;
|
|
||||||
|
|
||||||
History() = default;
|
|
||||||
virtual ~History() = default;
|
|
||||||
History(const History&) = delete;
|
|
||||||
History(History&&) = delete;
|
|
||||||
History& operator=(const History&) = delete;
|
|
||||||
History& operator=(History&&) = delete;
|
|
||||||
|
|
||||||
virtual Command& Add(std::unique_ptr<Command>&&) noexcept = 0;
|
|
||||||
|
|
||||||
virtual void UnDo() = 0;
|
|
||||||
virtual void ReDo() = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class History::Command {
|
|
||||||
public:
|
|
||||||
Command() = default;
|
|
||||||
virtual ~Command() = default;
|
|
||||||
Command(const Command&) = delete;
|
|
||||||
Command(Command&&) = delete;
|
|
||||||
Command& operator=(const Command&) = delete;
|
|
||||||
Command& operator=(Command&&) = delete;
|
|
||||||
|
|
||||||
virtual void Apply() = 0;
|
|
||||||
virtual void Revert() = 0;
|
|
||||||
|
|
||||||
void ExecApply(const std::shared_ptr<nf7::Context>& ctx) noexcept {
|
|
||||||
ctx->env().ExecSub(ctx, [this]() { Apply(); });
|
|
||||||
}
|
|
||||||
void ExecRevert(const std::shared_ptr<nf7::Context>& ctx) noexcept {
|
|
||||||
ctx->env().ExecSub(ctx, [this]() { Revert(); });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class History::CorruptException : public Exception {
|
|
||||||
public:
|
|
||||||
using Exception::Exception;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,76 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <cassert>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class Life final {
|
|
||||||
public:
|
|
||||||
class Ref;
|
|
||||||
|
|
||||||
Life() = delete;
|
|
||||||
Life(T& target) noexcept : ptr_(&target) {
|
|
||||||
}
|
|
||||||
~Life() noexcept {
|
|
||||||
if (data_) data_->ptr = nullptr;
|
|
||||||
}
|
|
||||||
Life(const Life&) = delete;
|
|
||||||
Life(Life&&) = delete;
|
|
||||||
Life& operator=(const Life&) = delete;
|
|
||||||
Life& operator=(Life&&) = delete;
|
|
||||||
|
|
||||||
private:
|
|
||||||
T* const ptr_;
|
|
||||||
|
|
||||||
struct Data final {
|
|
||||||
std::atomic<T*> ptr;
|
|
||||||
};
|
|
||||||
std::shared_ptr<Data> data_;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class Life<T>::Ref final {
|
|
||||||
public:
|
|
||||||
Ref() = default;
|
|
||||||
Ref(const Life& life) noexcept {
|
|
||||||
if (!life.data_) {
|
|
||||||
auto& l = const_cast<Life&>(life);
|
|
||||||
l.data_ = std::make_shared<Data>();
|
|
||||||
l.data_->ptr = l.ptr_;
|
|
||||||
}
|
|
||||||
data_ = life.data_;
|
|
||||||
}
|
|
||||||
Ref(const Ref&) = default;
|
|
||||||
Ref(Ref&&) = default;
|
|
||||||
Ref& operator=(const Ref&) = default;
|
|
||||||
Ref& operator=(Ref&&) = default;
|
|
||||||
|
|
||||||
void EnforceAlive() const {
|
|
||||||
if (!data_->ptr) {
|
|
||||||
throw nf7::ExpiredException {"target expired"};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
operator bool() const noexcept {
|
|
||||||
return !!data_->ptr;
|
|
||||||
}
|
|
||||||
T& operator*() const noexcept {
|
|
||||||
assert(data_->ptr);
|
|
||||||
return *data_->ptr;
|
|
||||||
}
|
|
||||||
T* operator->() const noexcept {
|
|
||||||
return &**this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<Data> data_;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,52 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <exception>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
#include <source_location.hh>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class Logger : public File::Interface {
|
|
||||||
public:
|
|
||||||
enum Level {
|
|
||||||
kTrace,
|
|
||||||
kInfo,
|
|
||||||
kWarn,
|
|
||||||
kError,
|
|
||||||
};
|
|
||||||
struct Item;
|
|
||||||
|
|
||||||
Logger() = default;
|
|
||||||
|
|
||||||
// thread-safe
|
|
||||||
virtual void Write(Item&&) noexcept = 0;
|
|
||||||
|
|
||||||
// The parameter is to avoid problems with multi-inheritance and nothing more than.
|
|
||||||
virtual std::shared_ptr<Logger> self(Logger* = nullptr) noexcept = 0;
|
|
||||||
};
|
|
||||||
struct Logger::Item final {
|
|
||||||
public:
|
|
||||||
Item(Level lv, std::string_view m, File::Id f = 0, std::source_location s = std::source_location::current()) noexcept :
|
|
||||||
level(lv), msg(m), file(f), srcloc(s) {
|
|
||||||
}
|
|
||||||
Item(const Item&) = default;
|
|
||||||
Item(Item&&) = default;
|
|
||||||
Item& operator=(const Item&) = default;
|
|
||||||
Item& operator=(Item&&) = default;
|
|
||||||
|
|
||||||
Level level;
|
|
||||||
std::string msg;
|
|
||||||
|
|
||||||
File::Id file;
|
|
||||||
std::source_location srcloc;
|
|
||||||
|
|
||||||
std::exception_ptr ex;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,90 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <exception>
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
#include <source_location.hh>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
#include "common/file_base.hh"
|
|
||||||
#include "common/logger.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class LoggerRef final : public nf7::FileBase::Feature {
|
|
||||||
public:
|
|
||||||
LoggerRef(nf7::File& f, nf7::File::Path&& p = {"_logger"}) noexcept :
|
|
||||||
file_(&f), path_(std::move(p)) {
|
|
||||||
}
|
|
||||||
LoggerRef(const LoggerRef&) = default;
|
|
||||||
LoggerRef(LoggerRef&&) = default;
|
|
||||||
LoggerRef& operator=(const LoggerRef&) = default;
|
|
||||||
LoggerRef& operator=(LoggerRef&&) = default;
|
|
||||||
|
|
||||||
void Handle(const nf7::File::Event& ev) noexcept override {
|
|
||||||
std::unique_lock<std::mutex> k(mtx_);
|
|
||||||
switch (ev.type) {
|
|
||||||
case nf7::File::Event::kAdd:
|
|
||||||
try {
|
|
||||||
id_ = file_->id();
|
|
||||||
logger_ = file_->
|
|
||||||
ResolveUpwardOrThrow(path_).interfaceOrThrow<nf7::Logger>().self();
|
|
||||||
} catch (nf7::Exception&) {
|
|
||||||
id_ = 0;
|
|
||||||
logger_ = nullptr;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case nf7::File::Event::kRemove:
|
|
||||||
id_ = 0;
|
|
||||||
logger_ = nullptr;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// thread-safe
|
|
||||||
void Trace(std::string_view msg, std::source_location s = std::source_location::current()) noexcept {
|
|
||||||
Write({nf7::Logger::kTrace, msg, 0, s});
|
|
||||||
}
|
|
||||||
void Info(std::string_view msg, std::source_location s = std::source_location::current()) noexcept {
|
|
||||||
Write({nf7::Logger::kInfo, msg, 0, s});
|
|
||||||
}
|
|
||||||
void Info(nf7::Exception& e, std::source_location s = std::source_location::current()) noexcept {
|
|
||||||
Info(e.StringifyRecursive(), s);
|
|
||||||
}
|
|
||||||
void Warn(std::string_view msg, std::source_location s = std::source_location::current()) noexcept {
|
|
||||||
Write({nf7::Logger::kWarn, msg, 0, s});
|
|
||||||
}
|
|
||||||
void Warn(nf7::Exception& e, std::source_location s = std::source_location::current()) noexcept {
|
|
||||||
Warn(e.StringifyRecursive(), s);
|
|
||||||
}
|
|
||||||
void Error(std::string_view msg, std::source_location s = std::source_location::current()) noexcept {
|
|
||||||
Write({nf7::Logger::kError, msg, 0, s});
|
|
||||||
}
|
|
||||||
void Error(nf7::Exception& e, std::source_location s = std::source_location::current()) noexcept {
|
|
||||||
Error(e.StringifyRecursive(), s);
|
|
||||||
}
|
|
||||||
void Write(nf7::Logger::Item&& item) noexcept {
|
|
||||||
std::unique_lock<std::mutex> k(mtx_);
|
|
||||||
if (!id_ || !logger_) return;
|
|
||||||
item.file = id_;
|
|
||||||
item.ex = std::current_exception();
|
|
||||||
logger_->Write(std::move(item));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
nf7::File* const file_;
|
|
||||||
const nf7::File::Path path_;
|
|
||||||
|
|
||||||
std::mutex mtx_;
|
|
||||||
File::Id id_;
|
|
||||||
std::shared_ptr<nf7::Logger> logger_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
620
common/luajit.cc
620
common/luajit.cc
@ -1,620 +0,0 @@
|
|||||||
#include "common/luajit.hh"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cassert>
|
|
||||||
#include <chrono>
|
|
||||||
#include <cinttypes>
|
|
||||||
#include <cctype>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
#include <lua.hpp>
|
|
||||||
|
|
||||||
#include "common/logger.hh"
|
|
||||||
#include "common/logger_ref.hh"
|
|
||||||
#include "common/luajit_thread.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::luajit {
|
|
||||||
|
|
||||||
static void PushStd(lua_State* L) noexcept;
|
|
||||||
|
|
||||||
// buffer <-> lua value conversion
|
|
||||||
template <typename T>
|
|
||||||
static size_t PushArrayFromBytes(
|
|
||||||
lua_State* L, size_t n, const uint8_t* ptr, const uint8_t* end);
|
|
||||||
template <typename T>
|
|
||||||
static size_t PushFromBytes(lua_State* L, const uint8_t* ptr, const uint8_t* end);
|
|
||||||
template <typename T>
|
|
||||||
static size_t ToBytes(lua_State* L, uint8_t* ptr, uint8_t* end);
|
|
||||||
|
|
||||||
|
|
||||||
void PushGlobalTable(lua_State* L) noexcept {
|
|
||||||
if (luaL_newmetatable(L, "nf7::luajit::PushGlobalTable")) {
|
|
||||||
PushStd(L);
|
|
||||||
lua_setfield(L, -2, "std");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void PushImmEnv(lua_State* L) noexcept {
|
|
||||||
if (luaL_newmetatable(L, "nf7::luajit::PushImmEnv")) {
|
|
||||||
lua_createtable(L, 0, 0);
|
|
||||||
PushGlobalTable(L);
|
|
||||||
lua_setfield(L, -2, "__index");
|
|
||||||
|
|
||||||
lua_pushcfunction(L, [](auto L) { return luaL_error(L, "global is immutable"); });
|
|
||||||
lua_setfield(L, -2, "__newindex");
|
|
||||||
lua_setmetatable(L, -2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PushValue(lua_State* L, const nf7::Value& v) noexcept {
|
|
||||||
new (lua_newuserdata(L, sizeof(v))) nf7::Value(v);
|
|
||||||
|
|
||||||
if (luaL_newmetatable(L, "nf7::Value")) {
|
|
||||||
lua_createtable(L, 0, 0);
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
const auto& v = CheckRef<nf7::Value>(L, 1, "nf7::Value");
|
|
||||||
lua_pushstring(L, v.typeName());
|
|
||||||
return 1;
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "type");
|
|
||||||
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
const auto& v = CheckRef<nf7::Value>(L, 1, "nf7::Value");
|
|
||||||
|
|
||||||
struct Visitor final {
|
|
||||||
lua_State* L;
|
|
||||||
const nf7::Value& v;
|
|
||||||
auto operator()(Value::Pulse) noexcept { lua_pushnil(L); }
|
|
||||||
auto operator()(Value::Boolean) noexcept { lua_pushboolean(L, v.boolean()); }
|
|
||||||
auto operator()(Value::Integer) noexcept { lua_pushinteger(L, v.integer()); }
|
|
||||||
auto operator()(Value::Scalar) noexcept { lua_pushnumber(L, v.scalar()); }
|
|
||||||
auto operator()(Value::String) noexcept { lua_pushstring(L, v.string().c_str()); }
|
|
||||||
auto operator()(Value::Vector) noexcept { PushVector(L, v.vector()); }
|
|
||||||
auto operator()(Value::DataPtr) noexcept { lua_pushnil(L); }
|
|
||||||
|
|
||||||
auto operator()(Value::Tuple) noexcept {
|
|
||||||
const auto& tup = *v.tuple();
|
|
||||||
lua_createtable(L, 0, 0);
|
|
||||||
size_t arridx = 0;
|
|
||||||
for (auto& p : tup) {
|
|
||||||
PushValue(L, p.second);
|
|
||||||
if (p.first.empty()) {
|
|
||||||
lua_rawseti(L, -2, static_cast<int>(arridx++));
|
|
||||||
} else {
|
|
||||||
lua_setfield(L, -2, p.first.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
v.Visit(Visitor{.L = L, .v = v});
|
|
||||||
return 1;
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "value");
|
|
||||||
lua_setfield(L, -2, "__index");
|
|
||||||
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
CheckRef<nf7::Value>(L, 1, "nf7::Value").~Value();
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "__gc");
|
|
||||||
}
|
|
||||||
lua_setmetatable(L, -2);
|
|
||||||
}
|
|
||||||
void PushVector(lua_State* L, const nf7::Value::ConstVector& v) noexcept {
|
|
||||||
assert(v);
|
|
||||||
new (lua_newuserdata(L, sizeof(v))) nf7::Value::ConstVector(v);
|
|
||||||
|
|
||||||
if (luaL_newmetatable(L, "nf7::Value::ConstVector")) {
|
|
||||||
lua_createtable(L, 0, 0);
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
const auto& v = CheckRef<nf7::Value::ConstVector>(L, 1, "nf7::Value::ConstVector");
|
|
||||||
const auto offset = luaL_checkinteger(L, 2);
|
|
||||||
if (offset < 0) {
|
|
||||||
return luaL_error(L, "negative offset");
|
|
||||||
}
|
|
||||||
if (offset > static_cast<lua_Integer>(v->size())) {
|
|
||||||
return luaL_error(L, "offset overflow");
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t* ptr = v->data() + offset;
|
|
||||||
const uint8_t* end = v->data() + v->size();
|
|
||||||
|
|
||||||
luaL_checktype(L, 3, LUA_TTABLE);
|
|
||||||
const int ecnt = static_cast<int>(lua_objlen(L, 3));
|
|
||||||
lua_createtable(L, ecnt, 0);
|
|
||||||
|
|
||||||
for (int i = 1; i <= ecnt; ++i) {
|
|
||||||
lua_rawgeti(L, 3, i);
|
|
||||||
if (lua_istable(L, -1)) { // array
|
|
||||||
lua_rawgeti(L, -1, 1);
|
|
||||||
const std::string_view type = luaL_checkstring(L, -1);
|
|
||||||
lua_rawgeti(L, -2, 2);
|
|
||||||
const size_t n = static_cast<size_t>(luaL_checkinteger(L, -1));
|
|
||||||
lua_pop(L, 2);
|
|
||||||
|
|
||||||
if (type == "u8") {
|
|
||||||
ptr += PushArrayFromBytes<uint8_t>(L, n, ptr, end);
|
|
||||||
} else if (type == "u16") {
|
|
||||||
ptr += PushArrayFromBytes<uint16_t>(L, n, ptr, end);
|
|
||||||
} else if (type == "u32") {
|
|
||||||
ptr += PushArrayFromBytes<uint32_t>(L, n, ptr, end);
|
|
||||||
} else if (type == "u64") {
|
|
||||||
ptr += PushArrayFromBytes<uint64_t>(L, n, ptr, end);
|
|
||||||
} else if (type == "s8") {
|
|
||||||
ptr += PushArrayFromBytes<int8_t>(L, n, ptr, end);
|
|
||||||
} else if (type == "s16") {
|
|
||||||
ptr += PushArrayFromBytes<int16_t>(L, n, ptr, end);
|
|
||||||
} else if (type == "s32") {
|
|
||||||
ptr += PushArrayFromBytes<int32_t>(L, n, ptr, end);
|
|
||||||
} else if (type == "s64") {
|
|
||||||
ptr += PushArrayFromBytes<int64_t>(L, n, ptr, end);
|
|
||||||
} else if (type == "f32") {
|
|
||||||
ptr += PushArrayFromBytes<float>(L, n, ptr, end);
|
|
||||||
} else if (type == "f64") {
|
|
||||||
ptr += PushArrayFromBytes<double>(L, n, ptr, end);
|
|
||||||
}
|
|
||||||
} else if (lua_isstring(L, -1)) { // single
|
|
||||||
const std::string_view type = lua_tostring(L, -1);
|
|
||||||
if (type == "u8") {
|
|
||||||
ptr += PushFromBytes<uint8_t>(L, ptr, end);
|
|
||||||
} else if (type == "u16") {
|
|
||||||
ptr += PushFromBytes<uint16_t>(L, ptr, end);
|
|
||||||
} else if (type == "u32") {
|
|
||||||
ptr += PushFromBytes<uint32_t>(L, ptr, end);
|
|
||||||
} else if (type == "u64") {
|
|
||||||
ptr += PushFromBytes<uint64_t>(L, ptr, end);
|
|
||||||
} else if (type == "s8") {
|
|
||||||
ptr += PushFromBytes<int8_t>(L, ptr, end);
|
|
||||||
} else if (type == "s16") {
|
|
||||||
ptr += PushFromBytes<int16_t>(L, ptr, end);
|
|
||||||
} else if (type == "s32") {
|
|
||||||
ptr += PushFromBytes<int32_t>(L, ptr, end);
|
|
||||||
} else if (type == "s64") {
|
|
||||||
ptr += PushFromBytes<int64_t>(L, ptr, end);
|
|
||||||
} else if (type == "f32") {
|
|
||||||
ptr += PushFromBytes<float>(L, ptr, end);
|
|
||||||
} else if (type == "f64") {
|
|
||||||
ptr += PushFromBytes<double>(L, ptr, end);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return luaL_error(L, "unknown type specifier at index: %d", i);
|
|
||||||
}
|
|
||||||
lua_rawseti(L, -3, i);
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "get");
|
|
||||||
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
const auto& v = CheckRef<nf7::Value::ConstVector>(L, 1, "nf7::Value::ConstVector");
|
|
||||||
lua_pushlstring(L, reinterpret_cast<const char*>(v->data()), v->size());
|
|
||||||
return 1;
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "str");
|
|
||||||
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
const auto& v = CheckRef<nf7::Value::ConstVector>(L, 1, "nf7::Value::ConstVector");
|
|
||||||
lua_pushinteger(L, static_cast<lua_Integer>(v->size()));
|
|
||||||
return 1;
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "size");
|
|
||||||
lua_setfield(L, -2, "__index");
|
|
||||||
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
CheckRef<nf7::Value::ConstVector>(L, 1, "nf7::Value::ConstVector").~shared_ptr();
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "__gc");
|
|
||||||
}
|
|
||||||
lua_setmetatable(L, -2);
|
|
||||||
}
|
|
||||||
void PushMutableVector(lua_State* L, std::vector<uint8_t>&& v) noexcept {
|
|
||||||
new (lua_newuserdata(L, sizeof(v))) std::vector<uint8_t>(std::move(v));
|
|
||||||
|
|
||||||
if (luaL_newmetatable(L, "nf7::Value::MutableVector")) {
|
|
||||||
lua_createtable(L, 0, 0);
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
auto& v = CheckRef<std::vector<uint8_t>>(L, 1, "nf7::Value::MutableVector");
|
|
||||||
const lua_Integer offset = luaL_checkinteger(L, 2);
|
|
||||||
if (offset < 0) return luaL_error(L, "negative offset");
|
|
||||||
|
|
||||||
luaL_checktype(L, 3, LUA_TTABLE);
|
|
||||||
const int len = static_cast<int>(lua_objlen(L, 3));
|
|
||||||
|
|
||||||
uint8_t* ptr = v.data() + offset;
|
|
||||||
uint8_t* end = v.data() + v.size();
|
|
||||||
|
|
||||||
for (int i = 1; i <= len; ++i) {
|
|
||||||
lua_rawgeti(L, 3, i);
|
|
||||||
lua_rawgeti(L, -1, 1);
|
|
||||||
lua_rawgeti(L, -2, 2);
|
|
||||||
|
|
||||||
const std::string_view type = lua_tostring(L, -2);
|
|
||||||
if (type == "u8") {
|
|
||||||
ptr += ToBytes<uint8_t>(L, ptr, end);
|
|
||||||
} else if (type == "u16") {
|
|
||||||
ptr += ToBytes<uint16_t>(L, ptr, end);
|
|
||||||
} else if (type == "u32") {
|
|
||||||
ptr += ToBytes<uint32_t>(L, ptr, end);
|
|
||||||
} else if (type == "u64") {
|
|
||||||
ptr += ToBytes<uint64_t>(L, ptr, end);
|
|
||||||
} else if (type == "s8") {
|
|
||||||
ptr += ToBytes<int8_t>(L, ptr, end);
|
|
||||||
} else if (type == "s16") {
|
|
||||||
ptr += ToBytes<int16_t>(L, ptr, end);
|
|
||||||
} else if (type == "s32") {
|
|
||||||
ptr += ToBytes<int32_t>(L, ptr, end);
|
|
||||||
} else if (type == "s64") {
|
|
||||||
ptr += ToBytes<int64_t>(L, ptr, end);
|
|
||||||
} else if (type == "f32") {
|
|
||||||
ptr += ToBytes<float>(L, ptr, end);
|
|
||||||
} else if (type == "f64") {
|
|
||||||
ptr += ToBytes<double>(L, ptr, end);
|
|
||||||
}
|
|
||||||
lua_pop(L, 3);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "set");
|
|
||||||
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
auto& v = CheckRef<std::vector<uint8_t>>(L, 1, "nf7::Value::MutableVector");
|
|
||||||
const lua_Integer size = luaL_checkinteger(L, 2);
|
|
||||||
if (size < 0) return luaL_error(L, "negative size");
|
|
||||||
v.resize(static_cast<size_t>(size));
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "resize");
|
|
||||||
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
auto& dst = CheckRef<std::vector<uint8_t>>(L, 1, "nf7::Value::MutableVector");
|
|
||||||
const auto dst_off = luaL_checkinteger(L, 2);
|
|
||||||
|
|
||||||
const std::vector<uint8_t>* src;
|
|
||||||
if (const auto& v = ToVector(L, 3)) {
|
|
||||||
src = &**v;
|
|
||||||
} else if (const auto& mv = ToMutableVector(L, 3)) {
|
|
||||||
src = &*mv;
|
|
||||||
} else {
|
|
||||||
return luaL_error(L, "#2 argument must be vector or mutable vector");
|
|
||||||
}
|
|
||||||
const auto src_off = luaL_checkinteger(L, 4);
|
|
||||||
|
|
||||||
const lua_Integer size = luaL_checkinteger(L, 5);
|
|
||||||
if (size < 0) {
|
|
||||||
return luaL_error(L, "negative size");
|
|
||||||
}
|
|
||||||
if (dst_off < 0 || static_cast<size_t>(dst_off+size) > dst.size()) {
|
|
||||||
return luaL_error(L, "dst out of bounds");
|
|
||||||
}
|
|
||||||
if (src_off < 0 || static_cast<size_t>(src_off+size) > src->size()) {
|
|
||||||
return luaL_error(L, "src out of bounds");
|
|
||||||
}
|
|
||||||
std::memcpy(dst. data()+static_cast<size_t>(dst_off),
|
|
||||||
src->data()+static_cast<size_t>(src_off),
|
|
||||||
static_cast<size_t>(size));
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "blit");
|
|
||||||
lua_setfield(L, -2, "__index");
|
|
||||||
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
CheckRef<std::vector<uint8_t>>(L, 1, "nf7::Value::MutableVector").~vector();
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "__gc");
|
|
||||||
}
|
|
||||||
lua_setmetatable(L, -2);
|
|
||||||
}
|
|
||||||
void PushNodeRootSelectLambda(
|
|
||||||
lua_State* L, const std::shared_ptr<nf7::NodeRootSelectLambda>& la) noexcept {
|
|
||||||
assert(la);
|
|
||||||
|
|
||||||
using T = std::shared_ptr<nf7::NodeRootSelectLambda>;
|
|
||||||
new (lua_newuserdata(L, sizeof(T))) T {la};
|
|
||||||
|
|
||||||
if (luaL_newmetatable(L, "nf7::NodeRootSelectLambda")) {
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
CheckRef<T>(L, 1, "nf7::NodeRootSelectLambda").~T();
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "__gc");
|
|
||||||
}
|
|
||||||
lua_setmetatable(L, -2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::optional<nf7::Value> ToValue(lua_State* L, int idx) noexcept {
|
|
||||||
if (lua_isnoneornil(L, idx)) {
|
|
||||||
return nf7::Value {nf7::Value::Pulse {}};
|
|
||||||
}
|
|
||||||
if (lua_isnumber(L, idx)) {
|
|
||||||
const double n = lua_tonumber(L, idx);
|
|
||||||
return nf7::Value {n};
|
|
||||||
}
|
|
||||||
if (lua_isboolean(L, idx)) {
|
|
||||||
return nf7::Value {bool {!!lua_toboolean(L, idx)}};
|
|
||||||
}
|
|
||||||
if (lua_isstring(L, idx)) {
|
|
||||||
size_t len;
|
|
||||||
const char* str = lua_tolstring(L, idx, &len);
|
|
||||||
return nf7::Value {std::string {str, len}};
|
|
||||||
}
|
|
||||||
if (auto vec = ToVector(L, idx)) {
|
|
||||||
return nf7::Value {std::move(*vec)};
|
|
||||||
}
|
|
||||||
if (auto vec = ToMutableVector(L, idx)) {
|
|
||||||
return nf7::Value {std::move(*vec)};
|
|
||||||
}
|
|
||||||
if (lua_istable(L, idx)) {
|
|
||||||
std::vector<nf7::Value::TuplePair> tup;
|
|
||||||
lua_pushnil(L);
|
|
||||||
while (lua_next(L, idx)) {
|
|
||||||
std::string name = "";
|
|
||||||
if (lua_isstring(L, -2)) {
|
|
||||||
name = lua_tostring(L, -2);
|
|
||||||
}
|
|
||||||
auto val = ToValue(L, -1);
|
|
||||||
if (!val) return std::nullopt;
|
|
||||||
tup.push_back({std::move(name), std::move(*val)});
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
|
||||||
return nf7::Value {std::move(tup)};
|
|
||||||
}
|
|
||||||
if (auto val = ToRef<nf7::Value>(L, idx, "nf7::Value")) {
|
|
||||||
return *val;
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
std::optional<nf7::Value::ConstVector> ToVector(lua_State* L, int idx) noexcept {
|
|
||||||
auto ptr = ToRef<nf7::Value::ConstVector>(L, idx, "nf7::Value::ConstVector");
|
|
||||||
if (!ptr) return std::nullopt;
|
|
||||||
return *ptr;
|
|
||||||
}
|
|
||||||
std::optional<std::vector<uint8_t>> ToMutableVector(lua_State* L, int idx) noexcept {
|
|
||||||
auto ptr = ToRef<std::vector<uint8_t>>(L, idx, "nf7::Value::MutableVector");
|
|
||||||
if (!ptr) return std::nullopt;
|
|
||||||
return std::move(*ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void PushStd(lua_State* L) noexcept {
|
|
||||||
lua_newuserdata(L, 0);
|
|
||||||
lua_createtable(L, 0, 0);
|
|
||||||
lua_createtable(L, 0, 0);
|
|
||||||
{
|
|
||||||
// ---- lua lib ----
|
|
||||||
|
|
||||||
// assert(expr[, msg])
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
if (lua_toboolean(L, 1)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (lua_gettop(L) >= 2) {
|
|
||||||
return luaL_error(L, lua_tostring(L, 2));
|
|
||||||
} else {
|
|
||||||
return luaL_error(L, "assertion failure");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "assert");
|
|
||||||
|
|
||||||
// error(msg)
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
return luaL_error(L, luaL_checkstring(L, 1));
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "error");
|
|
||||||
|
|
||||||
// load(str)
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
if (0 != luaL_loadstring(L, luaL_checkstring(L, 1))) {
|
|
||||||
return luaL_error(L, "lua.load error: %s", lua_tostring(L, -1));
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "load");
|
|
||||||
|
|
||||||
// pcall(func, args...) -> success, result
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
if (0 == lua_pcall(L, lua_gettop(L)-1, LUA_MULTRET, 0)) {
|
|
||||||
lua_pushboolean(L, true);
|
|
||||||
lua_insert(L, 1);
|
|
||||||
return lua_gettop(L);
|
|
||||||
} else {
|
|
||||||
lua_pushboolean(L, false);
|
|
||||||
lua_insert(L, 1);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "pcall");
|
|
||||||
|
|
||||||
|
|
||||||
// ---- math lib ----
|
|
||||||
|
|
||||||
// sin(theta)
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
lua_pushnumber(L, std::sin(luaL_checknumber(L, 1)));
|
|
||||||
return 1;
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "sin");
|
|
||||||
|
|
||||||
// cos(theta)
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
lua_pushnumber(L, std::cos(luaL_checknumber(L, 1)));
|
|
||||||
return 1;
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "cos");
|
|
||||||
|
|
||||||
// tan(slope)
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
lua_pushnumber(L, std::tan(luaL_checknumber(L, 1)));
|
|
||||||
return 1;
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "tan");
|
|
||||||
|
|
||||||
|
|
||||||
// ---- table lib ----
|
|
||||||
|
|
||||||
// meta(table, meta_table)
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
|
||||||
luaL_checktype(L, 2, LUA_TTABLE);
|
|
||||||
lua_settop(L, 2);
|
|
||||||
lua_setmetatable(L, 1);
|
|
||||||
return 1;
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "meta");
|
|
||||||
|
|
||||||
|
|
||||||
// ---- time lib ----
|
|
||||||
|
|
||||||
// now()
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
const auto now = nf7::Env::Clock::now().time_since_epoch();
|
|
||||||
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now);
|
|
||||||
lua_pushnumber(L, static_cast<double>(ms.count())/1000.);
|
|
||||||
return 1;
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "now");
|
|
||||||
|
|
||||||
|
|
||||||
// ---- value lib ----
|
|
||||||
|
|
||||||
// value(entity) -> value
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
if (lua_isstring(L, 2)) {
|
|
||||||
const auto type = std::string_view {lua_tostring(L, 2)};
|
|
||||||
if (type == "integer" || type == "int") {
|
|
||||||
PushValue(L, static_cast<nf7::Value::Integer>(luaL_checkinteger(L, 1)));
|
|
||||||
} else {
|
|
||||||
return luaL_error(L, "unknown type specifier: %s", type);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PushValue(L, CheckValue(L, 1));
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "value");
|
|
||||||
|
|
||||||
// mvector(vector or mutable vector) -> mutable vector
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
if (auto imm = ToVector(L, 1)) {
|
|
||||||
if (imm->use_count() == 1) {
|
|
||||||
PushMutableVector(L, std::move(const_cast<std::vector<uint8_t>&>(**imm)));
|
|
||||||
} else {
|
|
||||||
PushMutableVector(L, std::vector<uint8_t> {**imm});
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
} else if (auto mut = ToMutableVector(L, 1)) {
|
|
||||||
PushMutableVector(L, std::vector<uint8_t> {*mut});
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
PushMutableVector(L, {});
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "mvector");
|
|
||||||
|
|
||||||
|
|
||||||
// ---- bit manip ----
|
|
||||||
luaL_openlibs(L);
|
|
||||||
luaL_loadstring(L, "return require(\"bit\")");
|
|
||||||
lua_call(L, 0, 1);
|
|
||||||
lua_setfield(L, -2, "bit");
|
|
||||||
}
|
|
||||||
lua_setfield(L, -2, "__index");
|
|
||||||
lua_setmetatable(L, -2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
static size_t PushArrayFromBytes(lua_State* L, size_t n, const uint8_t* ptr, const uint8_t* end) {
|
|
||||||
const size_t size = n*sizeof(T);
|
|
||||||
if (ptr + size > end) {
|
|
||||||
luaL_error(L, "bytes shortage");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
lua_createtable(L, static_cast<int>(n), 0);
|
|
||||||
for (size_t i = 0; i < n; ++i) {
|
|
||||||
if constexpr (std::is_integral<T>::value) {
|
|
||||||
lua_pushinteger(L, static_cast<lua_Integer>(*reinterpret_cast<const T*>(ptr)));
|
|
||||||
} else if constexpr (std::is_floating_point<T>::value) {
|
|
||||||
lua_pushnumber(L, static_cast<lua_Number>(*reinterpret_cast<const T*>(ptr)));
|
|
||||||
} else {
|
|
||||||
[] <bool F = false>() { static_assert(F, "T is invalid"); }();
|
|
||||||
}
|
|
||||||
lua_rawseti(L, -2, static_cast<int>(i + 1));
|
|
||||||
ptr += sizeof(T);
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
static size_t PushFromBytes(lua_State* L, const uint8_t* ptr, const uint8_t* end) {
|
|
||||||
const size_t size = sizeof(T);
|
|
||||||
if (ptr + size > end) {
|
|
||||||
luaL_error(L, "bytes shortage");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if constexpr (std::is_integral<T>::value) {
|
|
||||||
lua_pushinteger(L, static_cast<lua_Integer>(*reinterpret_cast<const T*>(ptr)));
|
|
||||||
} else if constexpr (std::is_floating_point<T>::value) {
|
|
||||||
lua_pushnumber(L, static_cast<lua_Number>(*reinterpret_cast<const T*>(ptr)));
|
|
||||||
} else {
|
|
||||||
[] <bool F = false>() { static_assert(F, "T is invalid"); }();
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
static size_t ToBytes(lua_State* L, uint8_t* ptr, uint8_t* end) {
|
|
||||||
if (lua_istable(L, -1)) {
|
|
||||||
const size_t len = lua_objlen(L, -1);
|
|
||||||
const size_t size = sizeof(T)*len;
|
|
||||||
if (ptr + size > end) {
|
|
||||||
luaL_error(L, "buffer size overflow");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < len; ++i) {
|
|
||||||
lua_rawgeti(L, -1, static_cast<int>(i+1));
|
|
||||||
if constexpr (std::is_integral<T>::value) {
|
|
||||||
*reinterpret_cast<T*>(ptr) = static_cast<T>(lua_tointeger(L, -1));
|
|
||||||
} else if constexpr (std::is_floating_point<T>::value) {
|
|
||||||
*reinterpret_cast<T*>(ptr) = static_cast<T>(lua_tonumber(L, -1));
|
|
||||||
} else {
|
|
||||||
[] <bool F = false>() { static_assert(F, "T is invalid"); }();
|
|
||||||
}
|
|
||||||
lua_pop(L, 1);
|
|
||||||
ptr += sizeof(T);
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
} else if (lua_isnumber(L, -1)) {
|
|
||||||
if (ptr + sizeof(T) > end) {
|
|
||||||
luaL_error(L, "buffer size overflow");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if constexpr (std::is_integral<T>::value) {
|
|
||||||
*reinterpret_cast<T*>(ptr) = static_cast<T>(lua_tointeger(L, -1));
|
|
||||||
} else if constexpr (std::is_floating_point<T>::value) {
|
|
||||||
*reinterpret_cast<T*>(ptr) = static_cast<T>(lua_tonumber(L, -1));
|
|
||||||
} else {
|
|
||||||
[] <bool F = false>() { static_assert(F, "T is invalid"); }();
|
|
||||||
}
|
|
||||||
return sizeof(T);
|
|
||||||
} else if (lua_isstring(L, -1)) {
|
|
||||||
if constexpr (std::is_same<T, uint8_t>::value) {
|
|
||||||
size_t sz;
|
|
||||||
const char* str = lua_tolstring(L, -1, &sz);
|
|
||||||
std::memcpy(ptr, str, std::min(static_cast<size_t>(end-ptr), sz));
|
|
||||||
return sz;
|
|
||||||
} else {
|
|
||||||
luaL_error(L, "string can be specified for only u8 type");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
luaL_error(L, "number or array expected");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nf7::luajit
|
|
140
common/luajit.hh
140
common/luajit.hh
@ -1,140 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <memory>
|
|
||||||
#include <optional>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <lua.hpp>
|
|
||||||
|
|
||||||
#include "common/node_root_select_lambda.hh"
|
|
||||||
#include "common/value.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::luajit {
|
|
||||||
|
|
||||||
void PushGlobalTable(lua_State*) noexcept;
|
|
||||||
void PushImmEnv(lua_State*) noexcept;
|
|
||||||
void PushValue(lua_State*, const nf7::Value&) noexcept;
|
|
||||||
void PushVector(lua_State*, const nf7::Value::ConstVector&) noexcept;
|
|
||||||
void PushMutableVector(lua_State*, std::vector<uint8_t>&&) noexcept;
|
|
||||||
void PushNodeRootSelectLambda(
|
|
||||||
lua_State*, const std::shared_ptr<nf7::NodeRootSelectLambda>&) noexcept;
|
|
||||||
|
|
||||||
std::optional<nf7::Value> ToValue(lua_State*, int) noexcept;
|
|
||||||
std::optional<nf7::Value::ConstVector> ToVector(lua_State*, int) noexcept;
|
|
||||||
std::optional<std::vector<uint8_t>> ToMutableVector(lua_State*, int) noexcept;
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void Push(lua_State* L, T v) noexcept {
|
|
||||||
if constexpr (std::is_integral<T>::value) {
|
|
||||||
lua_pushinteger(L, static_cast<lua_Integer>(v));
|
|
||||||
} else if constexpr (std::is_floating_point<T>::value) {
|
|
||||||
lua_pushnumber(L, static_cast<lua_Number>(v));
|
|
||||||
} else if constexpr (std::is_null_pointer<T>::value) {
|
|
||||||
lua_pushnil(L);
|
|
||||||
} else {
|
|
||||||
[] <bool F = false>() { static_assert(F, "T is invalid"); }();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inline void Push(lua_State* L, const std::string& v) noexcept {
|
|
||||||
lua_pushstring(L, v.c_str());
|
|
||||||
}
|
|
||||||
inline void Push(lua_State* L, const Value& v) noexcept {
|
|
||||||
luajit::PushValue(L, v);
|
|
||||||
}
|
|
||||||
inline void Push(lua_State* L, const nf7::Value::Vector& v) noexcept {
|
|
||||||
luajit::PushVector(L, v);
|
|
||||||
}
|
|
||||||
inline void Push(lua_State* L, const std::vector<uint8_t>& v) noexcept {
|
|
||||||
luajit::PushMutableVector(L, std::vector<uint8_t> {v});
|
|
||||||
}
|
|
||||||
inline void Push(lua_State* L, std::vector<uint8_t>&& v) noexcept {
|
|
||||||
luajit::PushMutableVector(L, std::move(v));
|
|
||||||
}
|
|
||||||
inline void Push(lua_State* L, const std::shared_ptr<nf7::NodeRootSelectLambda>& la) noexcept {
|
|
||||||
luajit::PushNodeRootSelectLambda(L, la);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int PushAll(lua_State*) noexcept {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
template <typename T, typename... Args>
|
|
||||||
int PushAll(lua_State* L, T v, Args&&... args) noexcept {
|
|
||||||
if constexpr (std::is_reference<T>::value) {
|
|
||||||
Push(L, std::forward<T>(v));
|
|
||||||
} else {
|
|
||||||
Push(L, v);
|
|
||||||
}
|
|
||||||
return 1+PushAll(L, std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
inline void PushWeakPtr(lua_State* L, const std::weak_ptr<T>& wptr) noexcept {
|
|
||||||
new (lua_newuserdata(L, sizeof(wptr))) std::weak_ptr<T>(wptr);
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
inline void PushWeakPtrDeleter(lua_State* L, const std::weak_ptr<T>& = {}) noexcept {
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
reinterpret_cast<std::weak_ptr<T>*>(lua_touserdata(L, 1))->~weak_ptr();
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool MatchMetaName(lua_State* L, int idx, const char* type) noexcept {
|
|
||||||
if (0 == lua_getmetatable(L, idx)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
luaL_getmetatable(L, type);
|
|
||||||
const bool ret = lua_rawequal(L, -1, -2);
|
|
||||||
lua_pop(L, 2);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
inline T* ToRef(lua_State* L, int idx, const char* type) noexcept {
|
|
||||||
return MatchMetaName(L, idx, type)? reinterpret_cast<T*>(lua_touserdata(L, idx)): nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
inline std::shared_ptr<T> CheckWeakPtr(lua_State* L, int idx, const char* type) {
|
|
||||||
auto ptr = reinterpret_cast<std::weak_ptr<T>*>(luaL_checkudata(L, idx, type));
|
|
||||||
if (auto ret = ptr->lock()) {
|
|
||||||
return ret;
|
|
||||||
} else {
|
|
||||||
luaL_error(L, "object expired: %s", typeid(T).name());
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
inline T& CheckRef(lua_State* L, int idx, const char* type) {
|
|
||||||
return *reinterpret_cast<T*>(luaL_checkudata(L, idx, type));
|
|
||||||
}
|
|
||||||
inline const std::shared_ptr<nf7::NodeRootSelectLambda>& CheckNodeRootSelectLambda(
|
|
||||||
lua_State* L, int idx) {
|
|
||||||
return CheckRef<std::shared_ptr<nf7::NodeRootSelectLambda>>(
|
|
||||||
L, idx, "nf7::NodeRootSelectLambda");
|
|
||||||
}
|
|
||||||
inline nf7::Value CheckValue(lua_State* L, int idx) {
|
|
||||||
auto v = ToValue(L, idx);
|
|
||||||
if (!v) luaL_error(L, "expected nf7::Value");
|
|
||||||
return std::move(*v);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void ToStringList(lua_State* L, std::vector<std::string>& v, int idx) {
|
|
||||||
const size_t n = lua_objlen(L, idx);
|
|
||||||
v.clear();
|
|
||||||
v.reserve(n);
|
|
||||||
for (int i = 1; i <= static_cast<int>(n); ++i) {
|
|
||||||
lua_rawgeti(L, idx, i);
|
|
||||||
if (auto str = lua_tostring(L, -1)) {
|
|
||||||
v.push_back(str);
|
|
||||||
}
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,31 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <lua.hpp>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::luajit {
|
|
||||||
|
|
||||||
class Queue : public File::Interface {
|
|
||||||
public:
|
|
||||||
using Task = std::function<void(lua_State*)>;
|
|
||||||
|
|
||||||
static constexpr auto kPath = "$/_luajit";
|
|
||||||
|
|
||||||
Queue() = default;
|
|
||||||
Queue(const Queue&) = delete;
|
|
||||||
Queue(Queue&&) = delete;
|
|
||||||
Queue& operator=(const Queue&) = delete;
|
|
||||||
Queue& operator=(Queue&&) = delete;
|
|
||||||
|
|
||||||
// thread-safe
|
|
||||||
virtual void Push(
|
|
||||||
const std::shared_ptr<nf7::Context>&, Task&&, nf7::Env::Time t = {}) noexcept = 0;
|
|
||||||
|
|
||||||
virtual std::shared_ptr<Queue> self() noexcept = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7::luajit
|
|
@ -1,47 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <lua.hpp>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
#include "common/luajit_queue.hh"
|
|
||||||
#include "common/value.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::luajit {
|
|
||||||
|
|
||||||
class Ref final : public nf7::Value::Data {
|
|
||||||
public:
|
|
||||||
Ref() = delete;
|
|
||||||
Ref(const std::shared_ptr<nf7::Context>& ctx,
|
|
||||||
const std::shared_ptr<nf7::luajit::Queue>& q, int idx) noexcept :
|
|
||||||
ctx_(ctx), q_(q), idx_(idx) {
|
|
||||||
}
|
|
||||||
Ref(const std::shared_ptr<nf7::Context>& ctx,
|
|
||||||
const std::shared_ptr<nf7::luajit::Queue>& q, lua_State* L) noexcept :
|
|
||||||
ctx_(ctx), q_(q), idx_(luaL_ref(L, LUA_REGISTRYINDEX)) {
|
|
||||||
}
|
|
||||||
~Ref() noexcept {
|
|
||||||
q_->Push(ctx_, [idx = idx_](auto L) { luaL_unref(L, LUA_REGISTRYINDEX, idx); });
|
|
||||||
}
|
|
||||||
Ref(const Ref&) = delete;
|
|
||||||
Ref(Ref&&) = delete;
|
|
||||||
Ref& operator=(const Ref&) = delete;
|
|
||||||
Ref& operator=(Ref&&) = delete;
|
|
||||||
|
|
||||||
void PushSelf(lua_State* L) noexcept {
|
|
||||||
lua_rawgeti(L, LUA_REGISTRYINDEX, idx_);
|
|
||||||
}
|
|
||||||
|
|
||||||
int index() const noexcept { return idx_; }
|
|
||||||
const std::shared_ptr<nf7::luajit::Queue>& ljq() const noexcept { return q_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<nf7::Context> ctx_;
|
|
||||||
std::shared_ptr<nf7::luajit::Queue> q_;
|
|
||||||
int idx_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7::luajit
|
|
@ -1,286 +0,0 @@
|
|||||||
#include "common/luajit_thread.hh"
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <sstream>
|
|
||||||
#include <tuple>
|
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
#include "common/node.hh"
|
|
||||||
#include "common/node_root_select_lambda.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::luajit {
|
|
||||||
|
|
||||||
constexpr size_t kInstructionLimit = 10000000;
|
|
||||||
constexpr size_t kBufferSizeMax = 64 * 1024 * 1024;
|
|
||||||
|
|
||||||
|
|
||||||
// Pushes a metatable for Thread object, available on global table as 'nf7'.
|
|
||||||
static void PushMeta(lua_State*) noexcept;
|
|
||||||
|
|
||||||
|
|
||||||
lua_State* Thread::Init(lua_State* L) noexcept {
|
|
||||||
assert(state_ == kInitial);
|
|
||||||
|
|
||||||
th_ = lua_newthread(L);
|
|
||||||
th_ref_.emplace(ctx_, ljq_, L);
|
|
||||||
|
|
||||||
state_ = kPaused;
|
|
||||||
return th_;
|
|
||||||
}
|
|
||||||
void Thread::Resume(lua_State* L, int narg) noexcept {
|
|
||||||
std::unique_lock<std::mutex> k(mtx_);
|
|
||||||
|
|
||||||
if (state_ == kAborted) return;
|
|
||||||
assert(L == th_);
|
|
||||||
assert(state_ == kPaused);
|
|
||||||
|
|
||||||
static const auto kHook = [](auto L, auto) {
|
|
||||||
luaL_error(L, "reached instruction limit (<=1e7)");
|
|
||||||
};
|
|
||||||
lua_sethook(L, kHook, LUA_MASKCOUNT, kInstructionLimit);
|
|
||||||
|
|
||||||
// set global table
|
|
||||||
PushGlobalTable(L);
|
|
||||||
PushWeakPtr(L, weak_from_this());
|
|
||||||
PushMeta(L);
|
|
||||||
lua_setmetatable(L, -2);
|
|
||||||
lua_setfield(L, -2, "nf7");
|
|
||||||
lua_pop(L, 1);
|
|
||||||
|
|
||||||
state_ = kRunning;
|
|
||||||
k.unlock();
|
|
||||||
active_ = true;
|
|
||||||
const auto ret = lua_resume(L, narg);
|
|
||||||
active_ = false;
|
|
||||||
k.lock();
|
|
||||||
if (state_ == kAborted) return;
|
|
||||||
switch (ret) {
|
|
||||||
case 0:
|
|
||||||
state_ = kFinished;
|
|
||||||
break;
|
|
||||||
case LUA_YIELD:
|
|
||||||
state_ = kPaused;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
state_ = kAborted;
|
|
||||||
}
|
|
||||||
if (!std::exchange(skip_handle_, false)) {
|
|
||||||
handler_(*this, L);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Thread::Abort() noexcept {
|
|
||||||
std::unique_lock<std::mutex> k(mtx_);
|
|
||||||
state_ = kAborted;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Thread::Handler Thread::CreateNodeLambdaHandler(
|
|
||||||
const std::shared_ptr<nf7::Node::Lambda>& caller,
|
|
||||||
const std::shared_ptr<nf7::Node::Lambda>& callee) noexcept {
|
|
||||||
return [caller, callee](auto& th, auto L) {
|
|
||||||
switch (th.state()) {
|
|
||||||
case nf7::luajit::Thread::kPaused:
|
|
||||||
switch (lua_gettop(L)) {
|
|
||||||
case 0:
|
|
||||||
th.ExecResume(L);
|
|
||||||
return;
|
|
||||||
case 2:
|
|
||||||
if (auto v = nf7::luajit::ToValue(L, 2)) {
|
|
||||||
auto k = luaL_checkstring(L, 1);
|
|
||||||
caller->env().ExecSub(
|
|
||||||
caller, [caller, callee, k = std::string {k}, v = std::move(v)]() {
|
|
||||||
caller->Handle(k, *v, callee);
|
|
||||||
});
|
|
||||||
th.ExecResume(L);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
/* FALLTHROUGH */
|
|
||||||
default:
|
|
||||||
if (auto log = th.logger()) {
|
|
||||||
log->Warn("invalid use of yield, nf7:yield() or nf7:yield(name, value)");
|
|
||||||
}
|
|
||||||
th.ExecResume(L);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case nf7::luajit::Thread::kFinished:
|
|
||||||
return;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (auto log = th.logger()) {
|
|
||||||
log->Warn(std::string {"luajit execution error: "}+lua_tostring(L, -1));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void PushMeta(lua_State* L) noexcept {
|
|
||||||
if (luaL_newmetatable(L, Thread::kTypeName)) {
|
|
||||||
PushWeakPtrDeleter<Thread>(L);
|
|
||||||
lua_setfield(L, -2, "__gc");
|
|
||||||
|
|
||||||
lua_createtable(L, 0, 0);
|
|
||||||
{
|
|
||||||
// nf7:resolve(path)
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
auto th = Thread::GetPtr(L, 1);
|
|
||||||
auto base = th->ctx()->initiator();
|
|
||||||
|
|
||||||
std::string path = luaL_checkstring(L, 2);
|
|
||||||
th->env().ExecSub(th->ctx(), [th, L, base, path = std::move(path)]() {
|
|
||||||
try {
|
|
||||||
th->ExecResume(L, th->env().GetFileOrThrow(base).ResolveOrThrow(path).id());
|
|
||||||
} catch (nf7::File::NotFoundException&) {
|
|
||||||
th->ExecResume(L, 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
th->ExpectYield(L);
|
|
||||||
return lua_yield(L, 0);
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "resolve");
|
|
||||||
|
|
||||||
// nf7:ref(obj)
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
auto th = Thread::GetPtr(L, 1);
|
|
||||||
lua_pushvalue(L, 2);
|
|
||||||
|
|
||||||
auto ref = std::make_shared<nf7::luajit::Ref>(th->ctx(), th->ljq(), L);
|
|
||||||
PushValue(L, nf7::Value {std::move(ref)});
|
|
||||||
return 1;
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "ref");
|
|
||||||
|
|
||||||
// nf7:query(file_id, interface)
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
auto th = Thread::GetPtr(L, 1);
|
|
||||||
|
|
||||||
const auto id = luaL_checkinteger(L, 2);
|
|
||||||
std::string iface = luaL_checkstring(L, 3);
|
|
||||||
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 == "node") {
|
|
||||||
th->ExecResume(
|
|
||||||
L, nf7::NodeRootSelectLambda::Create(
|
|
||||||
th->ctx(), f.template interfaceOrThrow<nf7::Node>()));
|
|
||||||
} else {
|
|
||||||
throw nf7::Exception {"unknown interface: "+iface};
|
|
||||||
}
|
|
||||||
} catch (nf7::Exception& e) {
|
|
||||||
th->ExecResume(L, nullptr, e.msg());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
th->ExpectYield(L);
|
|
||||||
return lua_yield(L, 0);
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "query");
|
|
||||||
|
|
||||||
// nf7:sleep(sec)
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
auto th = Thread::GetPtr(L, 1);
|
|
||||||
const auto sec = luaL_checknumber(L, 2);
|
|
||||||
|
|
||||||
const auto time = nf7::Env::Clock::now() +
|
|
||||||
std::chrono::milliseconds(static_cast<uint64_t>(sec*1000));
|
|
||||||
th->ljq()->Push(th->ctx(), [th, L](auto) { th->ExecResume(L); }, time);
|
|
||||||
|
|
||||||
th->ExpectYield(L);
|
|
||||||
return lua_yield(L, 0);
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "sleep");
|
|
||||||
|
|
||||||
// nf7:send(obj, params...)
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
auto th = Thread::GetPtr(L, 1);
|
|
||||||
auto la = luajit::CheckNodeRootSelectLambda(L, 2);
|
|
||||||
la->ExecSend(luaL_checkstring(L, 3), luajit::CheckValue(L, 4));
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "send");
|
|
||||||
|
|
||||||
// nf7:recv(obj, params...)
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
auto th = Thread::GetPtr(L, 1);
|
|
||||||
auto la = luajit::CheckNodeRootSelectLambda(L, 2);
|
|
||||||
|
|
||||||
std::unordered_set<std::string> names;
|
|
||||||
if (lua_istable(L, 3)) {
|
|
||||||
lua_pushnil(L);
|
|
||||||
while (lua_next(L, 3)) {
|
|
||||||
if (lua_isstring(L, -1)) {
|
|
||||||
names.insert(lua_tostring(L, -1));
|
|
||||||
} else {
|
|
||||||
return luaL_error(L, "table contains non-string value");
|
|
||||||
}
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (int i = 3; i <= lua_gettop(L); ++i) {
|
|
||||||
names.insert(luaL_checkstring(L, i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto fu = la->Select(std::move(names));
|
|
||||||
if (fu.done()) {
|
|
||||||
try {
|
|
||||||
const auto& p = fu.value();
|
|
||||||
lua_pushstring(L, p.first.c_str());
|
|
||||||
luajit::PushValue(L, p.second);
|
|
||||||
return 2;
|
|
||||||
} catch (nf7::Exception& e) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fu.ThenIf([L, th](auto& p) {
|
|
||||||
th->ExecResume(L, p.first, p.second);
|
|
||||||
}).template Catch<nf7::Exception>(nullptr, [L, th](nf7::Exception&) {
|
|
||||||
th->ExecResume(L);
|
|
||||||
});
|
|
||||||
th->ExpectYield(L);
|
|
||||||
return lua_yield(L, 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "recv");
|
|
||||||
|
|
||||||
// nf7:yield(results...)
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
return lua_yield(L, lua_gettop(L)-1);
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "yield");
|
|
||||||
|
|
||||||
// logging functions
|
|
||||||
static const auto log_write = [](lua_State* L, nf7::Logger::Level lv) {
|
|
||||||
auto th = Thread::GetPtr(L, 1);
|
|
||||||
auto logger = th->logger();
|
|
||||||
if (!logger) return luaL_error(L, "logger is not installed on current thread");
|
|
||||||
|
|
||||||
const int n = lua_gettop(L);
|
|
||||||
std::stringstream st;
|
|
||||||
for (int i = 2; i <= n; ++i) {
|
|
||||||
if (auto msg = lua_tostring(L, i)) {
|
|
||||||
st << msg;
|
|
||||||
} else {
|
|
||||||
return luaL_error(L, "cannot stringify %s", luaL_typename(L, i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger->Write({lv, st.str()});
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
lua_pushcfunction(L, [](auto L) { return log_write(L, nf7::Logger::kTrace); });
|
|
||||||
lua_setfield(L, -2, "trace");
|
|
||||||
lua_pushcfunction(L, [](auto L) { return log_write(L, nf7::Logger::kInfo); });
|
|
||||||
lua_setfield(L, -2, "info");
|
|
||||||
lua_pushcfunction(L, [](auto L) { return log_write(L, nf7::Logger::kWarn); });
|
|
||||||
lua_setfield(L, -2, "warn");
|
|
||||||
lua_pushcfunction(L, [](auto L) { return log_write(L, nf7::Logger::kError); });
|
|
||||||
lua_setfield(L, -2, "error");
|
|
||||||
}
|
|
||||||
lua_setfield(L, -2, "__index");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nf7::luajit
|
|
@ -1,159 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <atomic>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <lua.hpp>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
#include "common/future.hh"
|
|
||||||
#include "common/logger_ref.hh"
|
|
||||||
#include "common/luajit.hh"
|
|
||||||
#include "common/luajit_ref.hh"
|
|
||||||
#include "common/node.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::luajit {
|
|
||||||
|
|
||||||
class Thread final : public std::enable_shared_from_this<Thread> {
|
|
||||||
public:
|
|
||||||
static constexpr const char* kTypeName = "nf7::luajit::Thread";
|
|
||||||
|
|
||||||
enum State { kInitial, kRunning, kPaused, kFinished, kAborted, };
|
|
||||||
using Handler = std::function<void(Thread&, lua_State*)>;
|
|
||||||
|
|
||||||
class Lambda;
|
|
||||||
|
|
||||||
class Exception final : public nf7::Exception {
|
|
||||||
public:
|
|
||||||
using nf7::Exception::Exception;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Creates a handler to finalize a promise.
|
|
||||||
template <typename T>
|
|
||||||
static inline Handler CreatePromiseHandler(
|
|
||||||
nf7::Future<T>::Promise& pro, std::function<T(lua_State*)>&&) noexcept;
|
|
||||||
|
|
||||||
// Creates a handler to emit yielded value to Node::Lambda.
|
|
||||||
static Handler CreateNodeLambdaHandler(
|
|
||||||
const std::shared_ptr<nf7::Node::Lambda>& caller,
|
|
||||||
const std::shared_ptr<nf7::Node::Lambda>& callee) noexcept;
|
|
||||||
|
|
||||||
// must be called on luajit thread
|
|
||||||
static std::shared_ptr<Thread> GetPtr(lua_State* L, int idx) {
|
|
||||||
auto th = CheckWeakPtr<Thread>(L, idx, kTypeName);
|
|
||||||
th->EnsureActive(L);
|
|
||||||
return th;
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread() = delete;
|
|
||||||
Thread(const std::shared_ptr<nf7::Context>& ctx,
|
|
||||||
const std::shared_ptr<nf7::luajit::Queue>& ljq,
|
|
||||||
Handler&& handler) noexcept :
|
|
||||||
ctx_(ctx), ljq_(ljq), handler_(std::move(handler)) {
|
|
||||||
}
|
|
||||||
Thread(const Thread&) = delete;
|
|
||||||
Thread(Thread&&) = delete;
|
|
||||||
Thread& operator=(const Thread&) = delete;
|
|
||||||
Thread& operator=(Thread&&) = delete;
|
|
||||||
|
|
||||||
void Install(const std::shared_ptr<nf7::LoggerRef>& logger) noexcept {
|
|
||||||
assert(state_ == kInitial);
|
|
||||||
logger_ = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
// must be called on luajit thread
|
|
||||||
lua_State* Init(lua_State* L) noexcept;
|
|
||||||
|
|
||||||
// must be called on luajit thread
|
|
||||||
// L must be a thread state, which is returned by Init().
|
|
||||||
void Resume(lua_State* L, int narg) noexcept;
|
|
||||||
|
|
||||||
// must be called on luajit thread
|
|
||||||
// handler_ won't be called on next yielding
|
|
||||||
void ExpectYield(lua_State*) noexcept {
|
|
||||||
skip_handle_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// must be called on luajit thread
|
|
||||||
void EnsureActive(lua_State* L) {
|
|
||||||
if (!active_) {
|
|
||||||
luaL_error(L, "thread is not active");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// thread-safe
|
|
||||||
void Abort() noexcept;
|
|
||||||
|
|
||||||
// queue a task that exec Resume()
|
|
||||||
// thread-safe
|
|
||||||
template <typename... Args>
|
|
||||||
void ExecResume(lua_State* L, Args&&... args) noexcept {
|
|
||||||
auto self = shared_from_this();
|
|
||||||
ljq_->Push(ctx_, [this, L, self, args...](auto) mutable {
|
|
||||||
Resume(L, luajit::PushAll(L, std::forward<Args>(args)...));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
nf7::Env& env() noexcept { return ctx_->env(); }
|
|
||||||
const std::shared_ptr<nf7::Context>& ctx() const noexcept { return ctx_; }
|
|
||||||
const std::shared_ptr<nf7::luajit::Queue>& ljq() const noexcept { return ljq_; }
|
|
||||||
const std::shared_ptr<nf7::LoggerRef>& logger() const noexcept { return logger_; }
|
|
||||||
State state() const noexcept { return state_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
// initialized by constructor
|
|
||||||
std::mutex mtx_;
|
|
||||||
|
|
||||||
std::shared_ptr<nf7::Context> ctx_;
|
|
||||||
std::shared_ptr<nf7::luajit::Queue> ljq_;
|
|
||||||
|
|
||||||
Handler handler_;
|
|
||||||
std::atomic<State> state_ = kInitial;
|
|
||||||
|
|
||||||
|
|
||||||
// initialized on Init()
|
|
||||||
lua_State* th_ = nullptr;
|
|
||||||
std::optional<nf7::luajit::Ref> th_ref_;
|
|
||||||
|
|
||||||
|
|
||||||
// installed features
|
|
||||||
std::shared_ptr<nf7::LoggerRef> logger_;
|
|
||||||
|
|
||||||
|
|
||||||
// mutable params
|
|
||||||
bool active_ = false; // true while executing lua_resume
|
|
||||||
bool skip_handle_ = false; // handler_ won't be called on next yield
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
Thread::Handler Thread::CreatePromiseHandler(
|
|
||||||
nf7::Future<T>::Promise& pro, std::function<T(lua_State*)>&& f) noexcept {
|
|
||||||
return [pro = pro, f = std::move(f)](auto& self, auto L) mutable {
|
|
||||||
switch (self.state()) {
|
|
||||||
case kPaused:
|
|
||||||
pro.template Throw<nf7::Exception>("unexpected yield");
|
|
||||||
break;
|
|
||||||
case kFinished:
|
|
||||||
pro.Wrap([&]() { return f(L); });
|
|
||||||
break;
|
|
||||||
case kAborted:
|
|
||||||
pro.template Throw<nf7::Exception>(lua_tostring(L, -1));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
throw 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nf7::luajit
|
|
@ -1,197 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "common/luajit_thread.hh"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <deque>
|
|
||||||
#include <memory>
|
|
||||||
#include <span>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "common/luajit.hh"
|
|
||||||
#include "common/node.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::luajit {
|
|
||||||
|
|
||||||
class Thread::Lambda final : public Thread::RegistryItem,
|
|
||||||
public std::enable_shared_from_this<Thread::Lambda> {
|
|
||||||
public:
|
|
||||||
static constexpr const char* kTypeName = "nf7::luajit::Thread::Lambda";
|
|
||||||
|
|
||||||
static void CreateAndPush(
|
|
||||||
lua_State* L, const std::shared_ptr<Thread>& th, nf7::File& f) {
|
|
||||||
auto la = std::make_shared<Thread::Lambda>(th, f.interfaceOrThrow<nf7::Node>());
|
|
||||||
th->ljq()->Push(th->ctx(), [L, th, la](auto) {
|
|
||||||
th->Register(L, la);
|
|
||||||
la->Push(L);
|
|
||||||
th->Resume(L, 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::shared_ptr<Thread::Lambda> GetPtr(lua_State* L, int idx) {
|
|
||||||
auto self = luajit::CheckWeakPtr<Thread::Lambda>(L, idx, kTypeName);
|
|
||||||
self->GetThread(L)->EnsureActive(L);
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
// must be created on main thread
|
|
||||||
explicit Lambda(const std::shared_ptr<Thread>& th, nf7::Node& n) noexcept;
|
|
||||||
|
|
||||||
void Push(lua_State* L) noexcept {
|
|
||||||
luajit::PushWeakPtr<Thread::Lambda>(L, shared_from_this());
|
|
||||||
PushMeta(L);
|
|
||||||
lua_setmetatable(L, -2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::weak_ptr<Thread> th_;
|
|
||||||
|
|
||||||
class Receiver;
|
|
||||||
std::shared_ptr<Receiver> recv_;
|
|
||||||
std::shared_ptr<Node::Lambda> la_;
|
|
||||||
|
|
||||||
|
|
||||||
std::shared_ptr<Thread> GetThread(lua_State* L) {
|
|
||||||
if (auto th = th_.lock()) {
|
|
||||||
return th;
|
|
||||||
} else {
|
|
||||||
luaL_error(L, "thread expired");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void PushMeta(lua_State* L) noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Receives an output from targetted lambda and Resumes the Thread.
|
|
||||||
class Thread::Lambda::Receiver final : public Node::Lambda,
|
|
||||||
public std::enable_shared_from_this<Thread::Lambda::Receiver> {
|
|
||||||
public:
|
|
||||||
static constexpr size_t kMaxQueue = 1024;
|
|
||||||
|
|
||||||
Receiver() = delete;
|
|
||||||
Receiver(nf7::Env& env, nf7::File::Id id) noexcept :
|
|
||||||
Node::Lambda(env, id, nullptr) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void Handle(std::string_view name, const nf7::Value& v,
|
|
||||||
const std::shared_ptr<Node::Lambda>&) noexcept override {
|
|
||||||
values_.emplace_back(name, v);
|
|
||||||
if (values_.size() > kMaxQueue) {
|
|
||||||
values_.pop_front();
|
|
||||||
}
|
|
||||||
std::unique_lock<std::mutex> k(mtx_);
|
|
||||||
ResumeIf();
|
|
||||||
}
|
|
||||||
|
|
||||||
// must be called on luajit thread
|
|
||||||
// Returns true and pushes results to Lua stack when a value is already queued.
|
|
||||||
bool Select(lua_State* L,const std::shared_ptr<Thread>& th, std::vector<std::string>&& names) noexcept {
|
|
||||||
std::unique_lock<std::mutex> k(mtx_);
|
|
||||||
L_ = L;
|
|
||||||
th_ = th;
|
|
||||||
waiting_ = std::move(names);
|
|
||||||
return ResumeIf(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::deque<std::pair<std::string, Value>> values_;
|
|
||||||
|
|
||||||
std::mutex mtx_;
|
|
||||||
lua_State* L_;
|
|
||||||
std::shared_ptr<Thread> th_;
|
|
||||||
std::vector<std::string> waiting_;
|
|
||||||
|
|
||||||
|
|
||||||
// don't forget to lock mtx_
|
|
||||||
bool ResumeIf(bool yielded = true) noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
Thread::Lambda::Lambda(const std::shared_ptr<Thread>& th, nf7::Node& n) noexcept :
|
|
||||||
th_(th),
|
|
||||||
recv_(new Receiver {th->env(), th->ctx()->initiator()}),
|
|
||||||
la_(n.CreateLambda(recv_)) {
|
|
||||||
}
|
|
||||||
void Thread::Lambda::PushMeta(lua_State* L) noexcept {
|
|
||||||
if (luaL_newmetatable(L, kTypeName)) {
|
|
||||||
lua_createtable(L, 0, 0);
|
|
||||||
|
|
||||||
// Lambda:send(name or idx, value)
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
auto self = GetPtr(L, 1);
|
|
||||||
|
|
||||||
auto name = lua_tostring(L, 2);;
|
|
||||||
auto val = luajit::CheckValue(L, 3);
|
|
||||||
|
|
||||||
auto th = self->GetThread(L);
|
|
||||||
th->env().ExecSub(th->ctx(), [self, th, L, name = std::move(name), val = std::move(val)]() mutable {
|
|
||||||
self->la_->Handle(name, std::move(val), self->recv_);
|
|
||||||
th->ExecResume(L);
|
|
||||||
});
|
|
||||||
|
|
||||||
th->ExpectYield(L);
|
|
||||||
return lua_yield(L, 0);
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "send");
|
|
||||||
|
|
||||||
// Lambda:recv(handler)
|
|
||||||
lua_pushcfunction(L, [](auto L) {
|
|
||||||
auto self = GetPtr(L, 1);
|
|
||||||
|
|
||||||
std::vector<std::string> names = {};
|
|
||||||
if (lua_istable(L, 2)) {
|
|
||||||
names.resize(lua_objlen(L, 2));
|
|
||||||
for (size_t i = 0; i < names.size(); ++i) {
|
|
||||||
lua_rawgeti(L, 2, static_cast<int>(i+1));
|
|
||||||
names[i] = lua_tostring(L, -1);
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
names.push_back(lua_tostring(L, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto th = self->GetThread(L);
|
|
||||||
if (self->recv_->Select(L, th, std::move(names))) {
|
|
||||||
return 2;
|
|
||||||
} else {
|
|
||||||
th->ExpectYield(L);
|
|
||||||
return lua_yield(L, 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
lua_setfield(L, -2, "recv");
|
|
||||||
|
|
||||||
lua_setfield(L, -2, "__index");
|
|
||||||
|
|
||||||
PushWeakPtrDeleter<Thread::Lambda>(L);
|
|
||||||
lua_setfield(L, -2, "__gc");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool Thread::Lambda::Receiver::ResumeIf(bool yielded) noexcept {
|
|
||||||
if (!th_) return false;
|
|
||||||
|
|
||||||
for (auto p = values_.begin(); p < values_.end(); ++p) {
|
|
||||||
auto itr = std::find(waiting_.begin(), waiting_.end(), p->first);
|
|
||||||
if (itr == waiting_.end()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (yielded) {
|
|
||||||
th_->ExecResume(L_, *itr, p->second);
|
|
||||||
} else {
|
|
||||||
luajit::PushAll(L_, *itr, p->second);
|
|
||||||
}
|
|
||||||
values_.erase(p);
|
|
||||||
waiting_ = {};
|
|
||||||
th_ = nullptr;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nf7::luajit
|
|
@ -1,51 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class Memento : public File::Interface {
|
|
||||||
public:
|
|
||||||
class Tag;
|
|
||||||
class CorruptException;
|
|
||||||
|
|
||||||
Memento() = default;
|
|
||||||
Memento(const Memento&) = delete;
|
|
||||||
Memento(Memento&&) = delete;
|
|
||||||
Memento& operator=(const Memento&) = delete;
|
|
||||||
Memento& operator=(Memento&&) = delete;
|
|
||||||
|
|
||||||
virtual std::shared_ptr<Tag> Save() noexcept = 0;
|
|
||||||
virtual void Restore(const std::shared_ptr<Tag>&) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Memento::Tag {
|
|
||||||
public:
|
|
||||||
using Id = uint64_t;
|
|
||||||
|
|
||||||
Tag() = delete;
|
|
||||||
Tag(Id id) noexcept : id_(id) {
|
|
||||||
}
|
|
||||||
virtual ~Tag() = default;
|
|
||||||
Tag(const Tag&) = default;
|
|
||||||
Tag(Tag&&) = default;
|
|
||||||
Tag& operator=(const Tag&) = delete;
|
|
||||||
Tag& operator=(Tag&&) = delete;
|
|
||||||
|
|
||||||
Id id() const noexcept { return id_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
Id id_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Memento::CorruptException : public Exception {
|
|
||||||
public:
|
|
||||||
using Exception::Exception;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,58 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "common/generic_history.hh"
|
|
||||||
#include "common/memento.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class MementoRecorder final {
|
|
||||||
public:
|
|
||||||
MementoRecorder() = delete;
|
|
||||||
MementoRecorder(nf7::Memento* mem) noexcept :
|
|
||||||
mem_(mem), tag_(mem? mem->Save(): nullptr) {
|
|
||||||
}
|
|
||||||
MementoRecorder(const MementoRecorder&) = delete;
|
|
||||||
MementoRecorder(MementoRecorder&&) = delete;
|
|
||||||
MementoRecorder& operator=(const MementoRecorder&) = delete;
|
|
||||||
MementoRecorder& operator=(MementoRecorder&&) = delete;
|
|
||||||
|
|
||||||
std::unique_ptr<nf7::History::Command> CreateCommandIf() noexcept {
|
|
||||||
if (mem_) {
|
|
||||||
auto ptag = std::exchange(tag_, mem_->Save());
|
|
||||||
if (ptag != tag_) {
|
|
||||||
return std::make_unique<RestoreCommand>(*this, ptag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
nf7::Memento* const mem_;
|
|
||||||
std::shared_ptr<nf7::Memento::Tag> tag_;
|
|
||||||
|
|
||||||
|
|
||||||
class RestoreCommand final : public nf7::History::Command {
|
|
||||||
public:
|
|
||||||
RestoreCommand(MementoRecorder& rec, const std::shared_ptr<nf7::Memento::Tag>& tag) noexcept :
|
|
||||||
rec_(&rec), tag_(tag) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void Apply() override { Exec(); }
|
|
||||||
void Revert() override { Exec(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
MementoRecorder* const rec_;
|
|
||||||
std::shared_ptr<nf7::Memento::Tag> tag_;
|
|
||||||
|
|
||||||
void Exec() {
|
|
||||||
auto& mem = *rec_->mem_;
|
|
||||||
rec_->tag_ = std::exchange(tag_, mem.Save());
|
|
||||||
mem.Restore(rec_->tag_);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,20 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "common/memento.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class MutableMemento : public nf7::Memento {
|
|
||||||
public:
|
|
||||||
MutableMemento() = default;
|
|
||||||
MutableMemento(const MutableMemento&) = delete;
|
|
||||||
MutableMemento(MutableMemento&&) = delete;
|
|
||||||
MutableMemento& operator=(const MutableMemento&) = delete;
|
|
||||||
MutableMemento& operator=(MutableMemento&&) = delete;
|
|
||||||
|
|
||||||
virtual void Commit() noexcept = 0;
|
|
||||||
virtual void CommitAmend() noexcept = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
109
common/mutex.hh
109
common/mutex.hh
@ -1,109 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <deque>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "common/future.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
// nf7::Mutex is not thread-safe.
|
|
||||||
class Mutex final {
|
|
||||||
public:
|
|
||||||
class Lock;
|
|
||||||
|
|
||||||
Mutex() noexcept {
|
|
||||||
}
|
|
||||||
~Mutex() noexcept;
|
|
||||||
|
|
||||||
nf7::Future<std::shared_ptr<Lock>> AcquireLock(bool ex = false) noexcept {
|
|
||||||
if (auto ret = TryAcquireLock(ex)) {
|
|
||||||
return {ret};
|
|
||||||
} else {
|
|
||||||
if (ex || pends_.size() == 0 || pends_.back().ex) {
|
|
||||||
pends_.push_back({.pro = {}, .ex = ex});
|
|
||||||
}
|
|
||||||
return pends_.back().pro.future();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::shared_ptr<Lock> TryAcquireLock(bool ex = false) noexcept {
|
|
||||||
auto k = TryAcquireLock_(ex);
|
|
||||||
if (k) {
|
|
||||||
onLock();
|
|
||||||
}
|
|
||||||
return k;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::function<void()> onLock = [](){};
|
|
||||||
std::function<void()> onUnlock = [](){};
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool ex_ = false;
|
|
||||||
std::weak_ptr<Lock> k_;
|
|
||||||
|
|
||||||
struct Item final {
|
|
||||||
nf7::Future<std::shared_ptr<Lock>>::Promise pro;
|
|
||||||
bool ex;
|
|
||||||
};
|
|
||||||
std::deque<Item> pends_;
|
|
||||||
|
|
||||||
|
|
||||||
std::shared_ptr<Lock> TryAcquireLock_(bool ex) noexcept {
|
|
||||||
if (auto k = k_.lock()) {
|
|
||||||
if (!ex_ && !ex) {
|
|
||||||
return k;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
k = std::make_shared<Lock>(*this);
|
|
||||||
ex_ = ex;
|
|
||||||
k_ = k;
|
|
||||||
return k;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Mutex::Lock final {
|
|
||||||
public:
|
|
||||||
friend nf7::Mutex;
|
|
||||||
|
|
||||||
Lock() = delete;
|
|
||||||
Lock(nf7::Mutex& mtx) noexcept : mtx_(&mtx) {
|
|
||||||
}
|
|
||||||
Lock(const Lock&) = delete;
|
|
||||||
Lock(Lock&&) = delete;
|
|
||||||
Lock& operator=(const Lock&) = delete;
|
|
||||||
Lock& operator=(Lock&&) = delete;
|
|
||||||
|
|
||||||
~Lock() noexcept {
|
|
||||||
if (mtx_) {
|
|
||||||
auto& pends = mtx_->pends_;
|
|
||||||
if (pends.size() > 0) {
|
|
||||||
auto item = std::move(pends.front());
|
|
||||||
pends.pop_front();
|
|
||||||
mtx_->ex_ = false;
|
|
||||||
mtx_->k_ = {};
|
|
||||||
|
|
||||||
auto k = mtx_->TryAcquireLock_(item.ex);
|
|
||||||
assert(k);
|
|
||||||
item.pro.Return(std::move(k));
|
|
||||||
} else {
|
|
||||||
mtx_->onUnlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
nf7::Mutex* mtx_;
|
|
||||||
};
|
|
||||||
Mutex::~Mutex() noexcept {
|
|
||||||
pends_.clear();
|
|
||||||
if (auto k = k_.lock()) {
|
|
||||||
k->mtx_ = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,54 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class NFile final {
|
|
||||||
public:
|
|
||||||
class Exception final : public nf7::Exception {
|
|
||||||
using nf7::Exception::Exception;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum Flag : uint8_t {
|
|
||||||
kRead = 1 << 0,
|
|
||||||
kWrite = 1 << 1,
|
|
||||||
};
|
|
||||||
using Flags = uint8_t;
|
|
||||||
|
|
||||||
NFile() = delete;
|
|
||||||
NFile(const std::filesystem::path& path, Flags flags) :
|
|
||||||
path_(path), flags_(flags) {
|
|
||||||
Init();
|
|
||||||
}
|
|
||||||
~NFile() noexcept;
|
|
||||||
NFile(const NFile&) = delete;
|
|
||||||
NFile(NFile&&) = delete;
|
|
||||||
NFile& operator=(const NFile&) = delete;
|
|
||||||
NFile& operator=(NFile&&) = delete;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
Flags flags() const noexcept {
|
|
||||||
return flags_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
const std::filesystem::path path_;
|
|
||||||
const Flags flags_;
|
|
||||||
|
|
||||||
uintptr_t handle_;
|
|
||||||
|
|
||||||
void Init();
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,69 +0,0 @@
|
|||||||
#include "common/nfile.hh"
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/file.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
void NFile::Init() {
|
|
||||||
int flags = 0;
|
|
||||||
if ((flags_ & kRead) && (flags_ & kWrite)) {
|
|
||||||
flags |= O_RDWR | O_CREAT;
|
|
||||||
} else if (flags_ & kRead) {
|
|
||||||
flags |= O_RDONLY;
|
|
||||||
} else if (flags_ & kWrite) {
|
|
||||||
flags |= O_WRONLY | O_CREAT;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fd = open(path_.string().c_str(), flags, 0600);
|
|
||||||
if (fd < 0) {
|
|
||||||
throw NFile::Exception {"open failure"};
|
|
||||||
}
|
|
||||||
handle_ = static_cast<uint64_t>(fd);
|
|
||||||
}
|
|
||||||
NFile::~NFile() noexcept {
|
|
||||||
const auto fd = static_cast<int>(handle_);
|
|
||||||
if (close(fd) == -1) {
|
|
||||||
// ;(
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t NFile::Read(size_t offset, uint8_t* buf, size_t size) {
|
|
||||||
const auto fd = static_cast<int>(handle_);
|
|
||||||
const auto off = static_cast<off_t>(offset);
|
|
||||||
if (lseek(fd, off, SEEK_SET) == off-1) {
|
|
||||||
throw NFile::Exception {"lseek failure"};
|
|
||||||
}
|
|
||||||
const auto ret = read(fd, buf, size);
|
|
||||||
if (ret == -1) {
|
|
||||||
throw NFile::Exception {"read failure"};
|
|
||||||
}
|
|
||||||
return static_cast<size_t>(ret);
|
|
||||||
}
|
|
||||||
size_t NFile::Write(size_t offset, const uint8_t* buf, size_t size) {
|
|
||||||
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::NFile::Exception {"lseek failure"};
|
|
||||||
}
|
|
||||||
const auto ret = write(fd, buf, size);
|
|
||||||
if (ret == -1) {
|
|
||||||
throw nf7::NFile::Exception {"write failure"};
|
|
||||||
}
|
|
||||||
return static_cast<size_t>(ret);
|
|
||||||
}
|
|
||||||
size_t NFile::Truncate(size_t size) {
|
|
||||||
const auto fd = static_cast<int>(handle_);
|
|
||||||
if (ftruncate(fd, static_cast<off_t>(size)) == 0) {
|
|
||||||
throw nf7::NFile::Exception {"ftruncate failure"};
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,46 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include "common/file_base.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class NFileWatcher final : public nf7::FileBase::Feature {
|
|
||||||
public:
|
|
||||||
NFileWatcher() = default;
|
|
||||||
NFileWatcher(const NFileWatcher&) = delete;
|
|
||||||
NFileWatcher(NFileWatcher&&) = delete;
|
|
||||||
NFileWatcher& operator=(const NFileWatcher&) = delete;
|
|
||||||
NFileWatcher& operator=(NFileWatcher&&) = delete;
|
|
||||||
|
|
||||||
void Watch(const std::filesystem::path& npath) noexcept {
|
|
||||||
npath_ = npath;
|
|
||||||
lastmod_ = std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::function<void()> onMod;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void Update() noexcept override
|
|
||||||
try {
|
|
||||||
if (npath_) {
|
|
||||||
const auto lastmod = std::filesystem::last_write_time(*npath_);
|
|
||||||
if (lastmod_ && lastmod > *lastmod_) {
|
|
||||||
onMod();
|
|
||||||
}
|
|
||||||
lastmod_ = lastmod;
|
|
||||||
}
|
|
||||||
} catch (std::filesystem::filesystem_error&) {
|
|
||||||
lastmod_.emplace();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::optional<std::filesystem::path> npath_;
|
|
||||||
std::optional<std::filesystem::file_time_type> lastmod_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,80 +0,0 @@
|
|||||||
#include "common/nfile.hh"
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include <windows.h>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
void NFile::Init() {
|
|
||||||
DWORD acc = 0;
|
|
||||||
DWORD flags = 0;
|
|
||||||
if (flags_ & kRead) {
|
|
||||||
acc |= GENERIC_READ;
|
|
||||||
flags |= OPEN_EXISTING;
|
|
||||||
}
|
|
||||||
if (flags_ & kWrite) {
|
|
||||||
acc |= GENERIC_WRITE;
|
|
||||||
flags |= OPEN_ALWAYS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
HANDLE h = CreateFileA(
|
|
||||||
path_.string().c_str(),
|
|
||||||
acc, 0, nullptr, flags, FILE_ATTRIBUTE_NORMAL, nullptr);
|
|
||||||
if (h == INVALID_HANDLE_VALUE) {
|
|
||||||
throw NFile::Exception {"open failure"};
|
|
||||||
}
|
|
||||||
handle_ = reinterpret_cast<uintptr_t>(h);
|
|
||||||
}
|
|
||||||
NFile::~NFile() noexcept {
|
|
||||||
auto h = reinterpret_cast<HANDLE>(handle_);
|
|
||||||
if (!CloseHandle(h)) {
|
|
||||||
// ;(
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t NFile::Read(size_t offset, uint8_t* buf, size_t size) {
|
|
||||||
const auto h = reinterpret_cast<HANDLE>(handle_);
|
|
||||||
|
|
||||||
LONG off_low = offset & 0xFFFFFFFF;
|
|
||||||
LONG off_high = offset >> 32;
|
|
||||||
if (INVALID_SET_FILE_POINTER == SetFilePointer(h, off_low, &off_high, FILE_BEGIN)) {
|
|
||||||
throw NFile::Exception {"failed to set file pointer"};
|
|
||||||
}
|
|
||||||
DWORD ret;
|
|
||||||
if (!ReadFile(h, buf, static_cast<DWORD>(size), &ret, nullptr)) {
|
|
||||||
throw NFile::Exception {"read failure"};
|
|
||||||
}
|
|
||||||
return static_cast<size_t>(ret);
|
|
||||||
}
|
|
||||||
size_t NFile::Write(size_t offset, const uint8_t* buf, size_t size) {
|
|
||||||
const auto h = reinterpret_cast<HANDLE>(handle_);
|
|
||||||
|
|
||||||
LONG off_low = offset & 0xFFFFFFFF;
|
|
||||||
LONG off_high = offset >> 32;
|
|
||||||
if (INVALID_SET_FILE_POINTER == SetFilePointer(h, off_low, &off_high, FILE_BEGIN)) {
|
|
||||||
throw NFile::Exception {"failed to set file pointer"};
|
|
||||||
}
|
|
||||||
DWORD ret;
|
|
||||||
if (!WriteFile(h, buf, static_cast<DWORD>(size), &ret, nullptr)) {
|
|
||||||
throw NFile::Exception {"read failure"};
|
|
||||||
}
|
|
||||||
return static_cast<size_t>(ret);
|
|
||||||
}
|
|
||||||
size_t NFile::Truncate(size_t size) {
|
|
||||||
const auto h = reinterpret_cast<HANDLE>(handle_);
|
|
||||||
|
|
||||||
LONG off_low = size & 0xFFFFFFFF;
|
|
||||||
LONG off_high = size >> 32;
|
|
||||||
if (INVALID_SET_FILE_POINTER == SetFilePointer(h, off_low, &off_high, FILE_BEGIN)) {
|
|
||||||
throw NFile::Exception {"failed to set file pointer"};
|
|
||||||
}
|
|
||||||
if (!SetEndOfFile(h)) {
|
|
||||||
throw NFile::Exception {"SetEndOfFile failure"};
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,94 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <memory>
|
|
||||||
#include <span>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
#include "common/value.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class Node : public File::Interface {
|
|
||||||
public:
|
|
||||||
class Editor;
|
|
||||||
class Lambda;
|
|
||||||
|
|
||||||
enum Flag : uint8_t {
|
|
||||||
kNone = 0,
|
|
||||||
kCustomNode = 1 << 0,
|
|
||||||
kMenu = 1 << 1,
|
|
||||||
kMenu_DirItem = 1 << 2, // use DirItem::UpdateMenu() method instead of Node's
|
|
||||||
};
|
|
||||||
using Flags = uint8_t;
|
|
||||||
|
|
||||||
Node(Flags f) noexcept : flags_(f) { }
|
|
||||||
Node(const Node&) = default;
|
|
||||||
Node(Node&&) = default;
|
|
||||||
Node& operator=(const Node&) = default;
|
|
||||||
Node& operator=(Node&&) = default;
|
|
||||||
|
|
||||||
virtual std::shared_ptr<Lambda> CreateLambda(const std::shared_ptr<Lambda>&) noexcept = 0;
|
|
||||||
|
|
||||||
virtual void UpdateNode(Editor&) noexcept { }
|
|
||||||
virtual void UpdateMenu(Editor&) noexcept { }
|
|
||||||
|
|
||||||
// The returned span is alive until next operation to the file.
|
|
||||||
virtual std::span<const std::string> GetInputs() const noexcept = 0;
|
|
||||||
virtual std::span<const std::string> GetOutputs() const noexcept = 0;
|
|
||||||
|
|
||||||
Flags flags() const noexcept { return flags_; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
Flags flags_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Node::Editor {
|
|
||||||
public:
|
|
||||||
Editor() = default;
|
|
||||||
virtual ~Editor() = default;
|
|
||||||
Editor(const Editor&) = delete;
|
|
||||||
Editor(Editor&&) = delete;
|
|
||||||
Editor& operator=(const Editor&) = delete;
|
|
||||||
Editor& operator=(Editor&&) = delete;
|
|
||||||
|
|
||||||
virtual void Emit(Node&, std::string_view, nf7::Value&&) noexcept = 0;
|
|
||||||
virtual std::shared_ptr<Lambda> GetLambda(Node& node) noexcept = 0;
|
|
||||||
|
|
||||||
virtual void AddLink(Node& src_node, std::string_view src_name,
|
|
||||||
Node& dst_node, std::string_view dst_name) noexcept = 0;
|
|
||||||
virtual void RemoveLink(Node& src_node, std::string_view src_name,
|
|
||||||
Node& dst_node, std::string_view dst_name) noexcept = 0;
|
|
||||||
|
|
||||||
virtual std::vector<std::pair<Node*, std::string>> GetSrcOf(Node&, std::string_view) const noexcept = 0;
|
|
||||||
virtual std::vector<std::pair<Node*, std::string>> GetDstOf(Node&, std::string_view) const noexcept = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Node::Lambda : public nf7::Context {
|
|
||||||
public:
|
|
||||||
Lambda(nf7::File& f, const std::shared_ptr<nf7::Context>& parent = nullptr) noexcept :
|
|
||||||
Lambda(f.env(), f.id(), parent) {
|
|
||||||
}
|
|
||||||
Lambda(nf7::Env& env, nf7::File::Id id, const std::shared_ptr<nf7::Context>& parent = nullptr) noexcept :
|
|
||||||
Context(env, id, parent),
|
|
||||||
parent_(std::dynamic_pointer_cast<Node::Lambda>(parent)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void Handle(
|
|
||||||
std::string_view, const nf7::Value&, const std::shared_ptr<Lambda>&) noexcept {
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Node::Lambda> parent() const noexcept { return parent_.lock(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::weak_ptr<Node::Lambda> parent_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,120 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <memory>
|
|
||||||
#include <span>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <yas/serialize.hpp>
|
|
||||||
#include <yas/types/std/string.hpp>
|
|
||||||
#include <yas/types/utility/usertype.hpp>
|
|
||||||
|
|
||||||
#include "common/aggregate_command.hh"
|
|
||||||
#include "common/history.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class NodeLinkStore {
|
|
||||||
public:
|
|
||||||
class SwapCommand;
|
|
||||||
|
|
||||||
struct Link {
|
|
||||||
public:
|
|
||||||
uint64_t src_id;
|
|
||||||
std::string src_name;
|
|
||||||
uint64_t dst_id;
|
|
||||||
std::string dst_name;
|
|
||||||
|
|
||||||
bool operator==(const Link& other) const noexcept {
|
|
||||||
if (src_id && other.src_id && src_id != other.src_id) return false;
|
|
||||||
if (dst_id && other.dst_id && dst_id != other.dst_id) return false;
|
|
||||||
if (src_name.size() && other.src_name.size() && src_name != other.src_name) return false;
|
|
||||||
if (dst_name.size() && other.dst_name.size() && dst_name != other.dst_name) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Ar>
|
|
||||||
Ar& serialize(Ar& ar) {
|
|
||||||
ar(src_id, src_name, dst_id, dst_name);
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
NodeLinkStore() = default;
|
|
||||||
NodeLinkStore(const NodeLinkStore&) = default;
|
|
||||||
NodeLinkStore(NodeLinkStore&&) = default;
|
|
||||||
NodeLinkStore& operator=(const NodeLinkStore&) = default;
|
|
||||||
NodeLinkStore& operator=(NodeLinkStore&&) = default;
|
|
||||||
|
|
||||||
template <typename Ar>
|
|
||||||
Ar& serialize(Ar& ar) {
|
|
||||||
ar(links_);
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddLink(Link&& lk) noexcept {
|
|
||||||
links_.push_back(std::move(lk));
|
|
||||||
}
|
|
||||||
void RemoveLink(const Link& lk) noexcept {
|
|
||||||
links_.erase(std::remove(links_.begin(), links_.end(), lk), links_.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::unique_ptr<nf7::History::Command> CreateCommandToRemoveExpired(
|
|
||||||
uint64_t id, std::span<const std::string> in, std::span<const std::string> out) noexcept;
|
|
||||||
|
|
||||||
std::span<const Link> items() const noexcept { return links_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<Link> links_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class NodeLinkStore::SwapCommand : public History::Command {
|
|
||||||
public:
|
|
||||||
static std::unique_ptr<SwapCommand> CreateToAdd(
|
|
||||||
NodeLinkStore& target, Link&& lk) noexcept {
|
|
||||||
return std::make_unique<SwapCommand>(target, std::move(lk), false);
|
|
||||||
}
|
|
||||||
static std::unique_ptr<SwapCommand> CreateToRemove(
|
|
||||||
NodeLinkStore& target, Link&& lk) noexcept {
|
|
||||||
return std::make_unique<SwapCommand>(target, std::move(lk), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
SwapCommand(NodeLinkStore& target, Link&& lk, bool added) noexcept :
|
|
||||||
target_(&target), link_(std::move(lk)), added_(added) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void Apply() noexcept override { Exec(); }
|
|
||||||
void Revert() noexcept override { Exec(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
NodeLinkStore* const target_;
|
|
||||||
Link link_;
|
|
||||||
bool added_;
|
|
||||||
|
|
||||||
void Exec() noexcept {
|
|
||||||
added_?
|
|
||||||
target_->RemoveLink(link_):
|
|
||||||
target_->AddLink(Link(link_));
|
|
||||||
added_ = !added_;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<nf7::History::Command> NodeLinkStore::CreateCommandToRemoveExpired(
|
|
||||||
uint64_t id, std::span<const std::string> in, std::span<const std::string> out) noexcept {
|
|
||||||
std::vector<std::unique_ptr<nf7::History::Command>> cmds;
|
|
||||||
for (const auto& lk : links_) {
|
|
||||||
const bool rm =
|
|
||||||
(lk.src_id == id && std::find(out.begin(), out.end(), lk.src_name) == out.end()) ||
|
|
||||||
(lk.dst_id == id && std::find(in .begin(), in .end(), lk.dst_name) == in .end());
|
|
||||||
if (rm) cmds.push_back(SwapCommand::CreateToRemove(*this, Link(lk)));
|
|
||||||
}
|
|
||||||
if (cmds.empty()) return nullptr;
|
|
||||||
return std::make_unique<nf7::AggregateCommand>(std::move(cmds));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,116 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
#include "common/future.hh"
|
|
||||||
#include "common/value.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class NodeRootLambda final : public nf7::Node::Lambda,
|
|
||||||
public std::enable_shared_from_this<NodeRootLambda> {
|
|
||||||
public:
|
|
||||||
struct Builder;
|
|
||||||
|
|
||||||
NodeRootLambda(const NodeRootLambda&) = delete;
|
|
||||||
NodeRootLambda(NodeRootLambda&&) = delete;
|
|
||||||
NodeRootLambda& operator=(const NodeRootLambda&) = delete;
|
|
||||||
NodeRootLambda& operator=(NodeRootLambda&&) = delete;
|
|
||||||
~NodeRootLambda() noexcept {
|
|
||||||
target_ = nullptr;
|
|
||||||
for (auto& pro : pro_) {
|
|
||||||
pro.second.Throw(std::make_exception_ptr(
|
|
||||||
nf7::Exception {"output was never satisified"}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<nf7::Node::Lambda> target_;
|
|
||||||
|
|
||||||
std::unordered_map<std::string, nf7::Future<nf7::Value>::Promise> pro_;
|
|
||||||
std::unordered_map<std::string, std::function<void(const nf7::Value&)>> handler_;
|
|
||||||
|
|
||||||
|
|
||||||
using nf7::Node::Lambda::Lambda;
|
|
||||||
void Handle(std::string_view name, const nf7::Value& v,
|
|
||||||
const std::shared_ptr<nf7::Node::Lambda>&) noexcept override {
|
|
||||||
const auto sname = std::string {name};
|
|
||||||
auto pitr = pro_.find(sname);
|
|
||||||
if (pitr != pro_.end()) {
|
|
||||||
pitr->second.Return(nf7::Value {v});
|
|
||||||
pro_.erase(pitr);
|
|
||||||
}
|
|
||||||
auto hitr = handler_.find(sname);
|
|
||||||
if (hitr != handler_.end()) {
|
|
||||||
hitr->second(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct NodeRootLambda::Builder final {
|
|
||||||
public:
|
|
||||||
Builder() = delete;
|
|
||||||
Builder(nf7::File& f, nf7::Node& n,
|
|
||||||
const std::shared_ptr<nf7::Context>& ctx = nullptr) noexcept :
|
|
||||||
prod_(new NodeRootLambda {f, ctx}), target_(n.CreateLambda(prod_)), node_(&n) {
|
|
||||||
prod_->target_ = target_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CheckOutput(std::string_view name) const {
|
|
||||||
auto out = node_->GetOutputs();
|
|
||||||
if (out.end() == std::find(out.begin(), out.end(), name)) {
|
|
||||||
throw nf7::Exception {"required output is missing: "+std::string {name}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void CheckInput(std::string_view name) const {
|
|
||||||
auto in = node_->GetInputs();
|
|
||||||
if (in.end() == std::find(in.begin(), in.end(), name)) {
|
|
||||||
throw nf7::Exception {"required input is missing: "+std::string {name}};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nf7::Future<nf7::Value> Receive(const std::string& name) {
|
|
||||||
assert(!built_);
|
|
||||||
CheckOutput(name);
|
|
||||||
auto [itr, added] =
|
|
||||||
prod_->pro_.try_emplace(name, nf7::Future<nf7::Value>::Promise {});
|
|
||||||
assert(added);
|
|
||||||
return itr->second.future();
|
|
||||||
}
|
|
||||||
void Listen(const std::string& name, std::function<void(const nf7::Value&)>&& f) {
|
|
||||||
assert(!built_);
|
|
||||||
CheckOutput(name);
|
|
||||||
prod_->handler_[name] = std::move(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<NodeRootLambda> Build() noexcept {
|
|
||||||
assert(!built_);
|
|
||||||
built_ = true;
|
|
||||||
return prod_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Send(std::string_view name, const nf7::Value& v) {
|
|
||||||
assert(built_);
|
|
||||||
CheckInput(name);
|
|
||||||
target_->Handle(name, v, prod_);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool built_ = false;
|
|
||||||
|
|
||||||
std::shared_ptr<NodeRootLambda> prod_;
|
|
||||||
|
|
||||||
std::shared_ptr<nf7::Node::Lambda> target_;
|
|
||||||
|
|
||||||
nf7::Node* const node_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,86 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
#include "common/future.hh"
|
|
||||||
#include "common/node.hh"
|
|
||||||
#include "common/value.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class NodeRootSelectLambda : public nf7::Node::Lambda,
|
|
||||||
public std::enable_shared_from_this<NodeRootSelectLambda> {
|
|
||||||
public:
|
|
||||||
using Pair = std::pair<std::string, nf7::Value>;
|
|
||||||
|
|
||||||
static std::shared_ptr<NodeRootSelectLambda> Create(
|
|
||||||
const std::shared_ptr<nf7::Context>& ctx, nf7::Node& n) noexcept {
|
|
||||||
auto ret = std::make_shared<NodeRootSelectLambda>(ctx->env(), ctx->initiator(), ctx);
|
|
||||||
ret->target_ = n.CreateLambda(ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
using nf7::Node::Lambda::Lambda;
|
|
||||||
|
|
||||||
void Handle(std::string_view k, const nf7::Value& v,
|
|
||||||
const std::shared_ptr<nf7::Node::Lambda>&) noexcept override {
|
|
||||||
std::unique_lock<std::mutex> lk(mtx_);
|
|
||||||
|
|
||||||
const auto ks = std::string {k};
|
|
||||||
if (names_.contains(ks)) {
|
|
||||||
names_.clear();
|
|
||||||
auto pro = *std::exchange(pro_, std::nullopt);
|
|
||||||
lk.unlock();
|
|
||||||
pro.Return({ks, v});
|
|
||||||
} else {
|
|
||||||
q_.push_back({ks, v});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// thread-safe
|
|
||||||
void ExecSend(std::string_view k, const nf7::Value& v) noexcept {
|
|
||||||
env().ExecSub(shared_from_this(), [this, k = std::string {k}, v = v]() {
|
|
||||||
target_->Handle(k, v, shared_from_this());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// thread-safe
|
|
||||||
nf7::Future<Pair> Select(std::unordered_set<std::string>&& names) noexcept {
|
|
||||||
std::unique_lock<std::mutex> k(mtx_);
|
|
||||||
assert(!pro_);
|
|
||||||
|
|
||||||
names_.clear();
|
|
||||||
for (auto itr = q_.begin(); itr < q_.end(); ++itr) {
|
|
||||||
if (names.contains(itr->first)) {
|
|
||||||
auto p = std::move(*itr);
|
|
||||||
q_.erase(itr);
|
|
||||||
k.unlock();
|
|
||||||
return {std::move(p)};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pro_.emplace();
|
|
||||||
names_ = std::move(names);
|
|
||||||
return pro_->future();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::mutex mtx_;
|
|
||||||
std::shared_ptr<nf7::Node::Lambda> target_;
|
|
||||||
|
|
||||||
std::vector<Pair> q_;
|
|
||||||
|
|
||||||
std::unordered_set<std::string> names_;
|
|
||||||
std::optional<nf7::Future<Pair>::Promise> pro_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,43 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <typeinfo>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
template <typename Base, typename... I>
|
|
||||||
struct PtrSelector final {
|
|
||||||
public:
|
|
||||||
PtrSelector(const std::type_info& t) noexcept : type_(&t) { }
|
|
||||||
PtrSelector(const PtrSelector&) = delete;
|
|
||||||
PtrSelector(PtrSelector&&) = delete;
|
|
||||||
PtrSelector& operator=(const PtrSelector&) = delete;
|
|
||||||
PtrSelector& operator=(PtrSelector&&) = delete;
|
|
||||||
|
|
||||||
template <typename T1, typename... T2>
|
|
||||||
Base* Select(T1 ptr1, T2... ptr2) noexcept {
|
|
||||||
auto ptr = Get<T1, I...>(ptr1);
|
|
||||||
return ptr? ptr: Select(ptr2...);
|
|
||||||
}
|
|
||||||
Base* Select() noexcept { return nullptr; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
template <typename T, typename I1, typename... I2>
|
|
||||||
Base* Get(T ptr) const noexcept {
|
|
||||||
if constexpr (std::is_base_of<I1, std::remove_pointer_t<T>>::value) {
|
|
||||||
if (*type_ == typeid(I1)) return static_cast<I1*>(ptr);
|
|
||||||
}
|
|
||||||
return Get<T, I2...>(ptr);
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
Base* Get(T) const noexcept { return nullptr; }
|
|
||||||
|
|
||||||
const std::type_info* const type_;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename... I>
|
|
||||||
using InterfaceSelector = PtrSelector<File::Interface, I...>;
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,53 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <deque>
|
|
||||||
#include <functional>
|
|
||||||
#include <mutex>
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
// thread-safe std::deque wrapper
|
|
||||||
template <typename T>
|
|
||||||
class Queue {
|
|
||||||
public:
|
|
||||||
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_);
|
|
||||||
++n_;
|
|
||||||
tasks_.push_back(std::move(task));
|
|
||||||
}
|
|
||||||
void Interrupt(T&& task) noexcept {
|
|
||||||
std::unique_lock<std::mutex> _(mtx_);
|
|
||||||
++n_;
|
|
||||||
tasks_.push_front(std::move(task));
|
|
||||||
}
|
|
||||||
std::optional<T> Pop() noexcept {
|
|
||||||
std::unique_lock<std::mutex> k(mtx_);
|
|
||||||
if (tasks_.empty()) return std::nullopt;
|
|
||||||
auto ret = std::move(tasks_.front());
|
|
||||||
tasks_.pop_front();
|
|
||||||
--n_;
|
|
||||||
k.unlock();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t size() const noexcept { return n_; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::mutex mtx_;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::atomic<size_t> n_;
|
|
||||||
|
|
||||||
std::deque<T> tasks_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,102 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
#include <tuple>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class RingBuffer final {
|
|
||||||
public:
|
|
||||||
RingBuffer() = delete;
|
|
||||||
RingBuffer(uint64_t unit, uint64_t bufn) noexcept :
|
|
||||||
buf_(unit*bufn), unit_(unit), bufn_(bufn) {
|
|
||||||
}
|
|
||||||
RingBuffer(const RingBuffer&) = delete;
|
|
||||||
RingBuffer(RingBuffer&&) = default;
|
|
||||||
RingBuffer& operator=(const RingBuffer&) = delete;
|
|
||||||
RingBuffer& operator=(RingBuffer&&) = default;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
uint64_t Mix(uint64_t begin, const T* ptr, uint64_t n) noexcept {
|
|
||||||
assert(unit_ == sizeof(T));
|
|
||||||
|
|
||||||
if (begin < cur_) {
|
|
||||||
const auto drop = cur_ - begin;
|
|
||||||
if (drop >= n) {
|
|
||||||
return cur_;
|
|
||||||
}
|
|
||||||
ptr += drop;
|
|
||||||
n -= drop;
|
|
||||||
begin = cur_;
|
|
||||||
}
|
|
||||||
if (begin > cur_) {
|
|
||||||
const auto skip = begin - cur_;
|
|
||||||
n = std::min(bufn_ - skip, n);
|
|
||||||
}
|
|
||||||
auto buf = reinterpret_cast<T*>(buf_.data());
|
|
||||||
|
|
||||||
const auto [c, r, l] = CalcCursor(begin, n);
|
|
||||||
for (uint64_t i = 0; i < r; ++i) {
|
|
||||||
buf[c+i] += ptr[i];
|
|
||||||
}
|
|
||||||
for (uint64_t i = 0; i < l; ++i) {
|
|
||||||
buf[i] += ptr[r+i];
|
|
||||||
}
|
|
||||||
return begin + n;
|
|
||||||
}
|
|
||||||
void Take(uint8_t* ptr, uint64_t n) noexcept {
|
|
||||||
const auto [c, r, l] = CalcCursor(cur_, n);
|
|
||||||
std::memcpy(&ptr[0*unit_], &buf_[c*unit_], r*unit_);
|
|
||||||
std::memcpy(&ptr[r*unit_], &buf_[0*unit_], l*unit_);
|
|
||||||
std::memset(&buf_[c*unit_], 0, r*unit_);
|
|
||||||
std::memset(&buf_[0*unit_], 0, l*unit_);
|
|
||||||
cur_ += n;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t Peek(uint64_t begin, uint8_t* ptr, uint64_t n) noexcept {
|
|
||||||
if (cur_ > bufn_) {
|
|
||||||
const auto actual_begin = std::max(begin, cur_-bufn_);
|
|
||||||
const auto pad = std::min(n, actual_begin - begin);
|
|
||||||
std::memset(ptr, 0, pad*unit_);
|
|
||||||
begin = actual_begin;
|
|
||||||
ptr += pad*unit_;
|
|
||||||
n -= pad;
|
|
||||||
}
|
|
||||||
n = std::min(n, bufn_);
|
|
||||||
|
|
||||||
const auto [c, r, l] = CalcCursor(begin, n);
|
|
||||||
std::memcpy(&ptr[0*unit_], &buf_[c*unit_], r*unit_);
|
|
||||||
std::memcpy(&ptr[r*unit_], &buf_[0*unit_], l*unit_);
|
|
||||||
return begin + n;
|
|
||||||
}
|
|
||||||
void Write(const uint8_t* ptr, uint64_t n) noexcept {
|
|
||||||
const auto [c, r, l] = CalcCursor(cur_, n);
|
|
||||||
std::memcpy(&buf_[c*unit_], &ptr[0*unit_], r*unit_);
|
|
||||||
std::memcpy(&buf_[0*unit_], &ptr[r*unit_], l*unit_);
|
|
||||||
cur_ += n;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t unit() const noexcept { return unit_; }
|
|
||||||
uint64_t bufn() const noexcept { return bufn_; }
|
|
||||||
uint64_t cur() const noexcept { return cur_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<uint8_t> buf_;
|
|
||||||
uint64_t unit_;
|
|
||||||
uint64_t bufn_;
|
|
||||||
uint64_t cur_ = 0;
|
|
||||||
|
|
||||||
std::tuple<uint64_t, uint64_t, uint64_t> CalcCursor(
|
|
||||||
uint64_t t, uint64_t n) noexcept {
|
|
||||||
assert(n <= bufn_);
|
|
||||||
const auto c = t % bufn_;
|
|
||||||
const auto r = std::min(bufn_ - c, n);
|
|
||||||
const auto l = n > r? n - r: 0;
|
|
||||||
return {c, r, l};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,114 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <memory>
|
|
||||||
#include <optional>
|
|
||||||
#include <span>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
#include "common/value.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class Sequencer : public nf7::File::Interface {
|
|
||||||
public:
|
|
||||||
class Editor;
|
|
||||||
class Session;
|
|
||||||
class Lambda;
|
|
||||||
|
|
||||||
enum Flag : uint8_t {
|
|
||||||
kNone = 0,
|
|
||||||
kCustomItem = 1 << 0, // uses UpdateItem() to draw an item on timeline if enable
|
|
||||||
kParamPanel = 1 << 1,
|
|
||||||
kTooltip = 1 << 2,
|
|
||||||
kMenu = 1 << 3,
|
|
||||||
};
|
|
||||||
using Flags = uint8_t;
|
|
||||||
|
|
||||||
Sequencer() = delete;
|
|
||||||
Sequencer(Flags flags) noexcept : flags_(flags) { }
|
|
||||||
Sequencer(const Sequencer&) = delete;
|
|
||||||
Sequencer(Sequencer&&) = delete;
|
|
||||||
Sequencer& operator=(const Sequencer&) = delete;
|
|
||||||
Sequencer& operator=(Sequencer&&) = delete;
|
|
||||||
|
|
||||||
// Sequencer* is a dummy parameter to avoid issues of multi inheritance.
|
|
||||||
virtual std::shared_ptr<Lambda> CreateLambda(
|
|
||||||
const std::shared_ptr<nf7::Context>&) noexcept = 0;
|
|
||||||
|
|
||||||
virtual void UpdateItem(Editor&) noexcept { }
|
|
||||||
virtual void UpdateParamPanel(Editor&) noexcept { }
|
|
||||||
virtual void UpdateTooltip(Editor&) noexcept { }
|
|
||||||
virtual void UpdateMenu(Editor&) noexcept { }
|
|
||||||
|
|
||||||
Flags flags() const noexcept { return flags_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
Flags flags_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Sequencer::Editor {
|
|
||||||
public:
|
|
||||||
Editor() noexcept = default;
|
|
||||||
virtual ~Editor() noexcept = default;
|
|
||||||
Editor(const Editor&) = delete;
|
|
||||||
Editor(Editor&&) = delete;
|
|
||||||
Editor& operator=(const Editor&) = delete;
|
|
||||||
Editor& operator=(Editor&&) = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Sequencer::Session {
|
|
||||||
public:
|
|
||||||
class UnknownNameException final : public nf7::Exception {
|
|
||||||
public:
|
|
||||||
using nf7::Exception::Exception;
|
|
||||||
};
|
|
||||||
|
|
||||||
Session() = default;
|
|
||||||
virtual ~Session() = default;
|
|
||||||
Session(const Session&) = delete;
|
|
||||||
Session(Session&&) = delete;
|
|
||||||
Session& operator=(const Session&) = delete;
|
|
||||||
Session& operator=(Session&&) = delete;
|
|
||||||
|
|
||||||
virtual const nf7::Value* Peek(std::string_view) noexcept = 0;
|
|
||||||
virtual std::optional<nf7::Value> Receive(std::string_view) noexcept = 0;
|
|
||||||
|
|
||||||
const nf7::Value& PeekOrThrow(std::string_view name) {
|
|
||||||
if (auto v = Peek(name)) {
|
|
||||||
return *v;
|
|
||||||
}
|
|
||||||
throw UnknownNameException {std::string {name}+" is unknown"};
|
|
||||||
}
|
|
||||||
nf7::Value ReceiveOrThrow(std::string_view name) {
|
|
||||||
if (auto v = Receive(name)) {
|
|
||||||
return std::move(*v);
|
|
||||||
}
|
|
||||||
throw UnknownNameException {std::string {name}+" is unknown"};
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void Send(std::string_view, nf7::Value&&) noexcept = 0;
|
|
||||||
|
|
||||||
// thread-safe
|
|
||||||
virtual void Finish() noexcept = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Sequencer::Lambda : public nf7::Context {
|
|
||||||
public:
|
|
||||||
Lambda(nf7::File& f, const std::shared_ptr<Context>& ctx = nullptr) noexcept :
|
|
||||||
Lambda(f.env(), f.id(), ctx) {
|
|
||||||
}
|
|
||||||
Lambda(nf7::Env& env, nf7::File::Id id,
|
|
||||||
const std::shared_ptr<nf7::Context>& ctx = nullptr) noexcept :
|
|
||||||
Context(env, id, ctx) {
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void Run(const std::shared_ptr<Sequencer::Session>&) noexcept = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,53 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <memory>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "common/aggregate_command.hh"
|
|
||||||
#include "common/generic_history.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class SquashedHistory : public nf7::GenericHistory {
|
|
||||||
public:
|
|
||||||
SquashedHistory() = default;
|
|
||||||
SquashedHistory(const SquashedHistory&) = delete;
|
|
||||||
SquashedHistory(SquashedHistory&&) = default;
|
|
||||||
SquashedHistory& operator=(const SquashedHistory&) = delete;
|
|
||||||
SquashedHistory& operator=(SquashedHistory&&) = default;
|
|
||||||
|
|
||||||
Command& Add(std::unique_ptr<Command>&& cmd) noexcept override {
|
|
||||||
staged_.push_back(std::move(cmd));
|
|
||||||
return *staged_.back();
|
|
||||||
}
|
|
||||||
bool Squash() noexcept {
|
|
||||||
if (staged_.size() == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
nf7::GenericHistory::Add(
|
|
||||||
std::make_unique<nf7::AggregateCommand>(std::move(staged_)));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Clear() noexcept {
|
|
||||||
nf7::GenericHistory::Clear();
|
|
||||||
staged_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnDo() override {
|
|
||||||
assert(staged_.size() == 0);
|
|
||||||
GenericHistory::UnDo();
|
|
||||||
}
|
|
||||||
void ReDo() override {
|
|
||||||
assert(staged_.size() == 0);
|
|
||||||
GenericHistory::ReDo();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<std::unique_ptr<Command>> staged_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,95 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <optional>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#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 Future = nf7::Future<T>;
|
|
||||||
using Coro = typename Future::Coro;
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
std::optional<Future>& fu() noexcept { return *fu_; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual Coro Proc() noexcept = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::optional<Coro> coro_;
|
|
||||||
std::optional<Future> fu_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// all operations are not thread-safe
|
|
||||||
template <typename T>
|
|
||||||
class Task<T>::Holder final {
|
|
||||||
public:
|
|
||||||
Holder() = default;
|
|
||||||
~Holder() noexcept {
|
|
||||||
Abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
Holder(const Holder&) = delete;
|
|
||||||
Holder(Holder&&) = delete;
|
|
||||||
Holder& operator=(const Holder&) = delete;
|
|
||||||
Holder& operator=(Holder&&) = delete;
|
|
||||||
|
|
||||||
bool CleanUp() noexcept {
|
|
||||||
return !!std::exchange(fu_, std::nullopt);
|
|
||||||
}
|
|
||||||
void Abort() noexcept {
|
|
||||||
if (auto task = task_.lock()) {
|
|
||||||
task->Abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename U, typename... Args>
|
|
||||||
nf7::Future<T> StartIf(Args&&... args) noexcept {
|
|
||||||
if (fu_) return *fu_;
|
|
||||||
|
|
||||||
auto task = std::make_shared<U>(std::forward<Args>(args)...);
|
|
||||||
task->Start();
|
|
||||||
|
|
||||||
task_ = task;
|
|
||||||
fu_ = task->fu();
|
|
||||||
return *fu_;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<nf7::Future<T>>& fu() noexcept { return fu_; }
|
|
||||||
const std::optional<nf7::Future<T>>& fu() const noexcept { return fu_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::weak_ptr<Task<T>> task_;
|
|
||||||
|
|
||||||
std::optional<nf7::Future<T>> fu_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,78 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
#include "common/timed_queue.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
// a thread emulation using nf7::Env::ExecAsync
|
|
||||||
template <typename Runner, typename Task>
|
|
||||||
class Thread final : public nf7::Context,
|
|
||||||
public std::enable_shared_from_this<Thread<Runner, Task>> {
|
|
||||||
public:
|
|
||||||
Thread() = delete;
|
|
||||||
Thread(nf7::File& f, Runner&& runner) noexcept :
|
|
||||||
Thread(f.env(), f.id(), std::move(runner)) {
|
|
||||||
}
|
|
||||||
Thread(nf7::Env& env, nf7::File::Id id, Runner&& runner) noexcept :
|
|
||||||
nf7::Context(env, id), env_(&env), runner_(std::move(runner)) {
|
|
||||||
}
|
|
||||||
Thread(const Thread&) = delete;
|
|
||||||
Thread(Thread&&) = delete;
|
|
||||||
Thread& operator=(const Thread&) = delete;
|
|
||||||
Thread& operator=(Thread&&) = delete;
|
|
||||||
|
|
||||||
void Push(const std::shared_ptr<nf7::Context>& ctx, Task&& t, nf7::Env::Time time = {}) noexcept {
|
|
||||||
q_.Push(time, {ctx, std::move(t)});
|
|
||||||
HandleNext(true /* = first */);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t tasksDone() const noexcept { return tasks_done_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
using Pair = std::pair<std::shared_ptr<nf7::Context>, Task>;
|
|
||||||
|
|
||||||
Env* const env_;
|
|
||||||
Runner runner_;
|
|
||||||
|
|
||||||
nf7::TimedQueue<Pair> q_;
|
|
||||||
|
|
||||||
std::mutex mtx_;
|
|
||||||
bool working_ = false;
|
|
||||||
|
|
||||||
std::atomic<size_t> tasks_done_ = 0;
|
|
||||||
|
|
||||||
|
|
||||||
using std::enable_shared_from_this<Thread<Runner, Task>>::shared_from_this;
|
|
||||||
void HandleNext(bool first = false) noexcept {
|
|
||||||
std::unique_lock<std::mutex> k(mtx_);
|
|
||||||
if (std::exchange(working_, true) && first) return;
|
|
||||||
|
|
||||||
auto self = shared_from_this();
|
|
||||||
if (auto p = q_.Pop()) {
|
|
||||||
k.unlock();
|
|
||||||
|
|
||||||
env_->ExecAsync(p->first, [this, self, t = std::move(p->second)]() mutable {
|
|
||||||
runner_(std::move(t));
|
|
||||||
++tasks_done_;
|
|
||||||
HandleNext();
|
|
||||||
});
|
|
||||||
} else if (auto time = q_.next()) {
|
|
||||||
working_ = false;
|
|
||||||
env_->ExecAsync(
|
|
||||||
shared_from_this(), [this, self]() mutable { HandleNext(); }, *time);
|
|
||||||
} else {
|
|
||||||
working_ = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,111 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <chrono>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <mutex>
|
|
||||||
#include <optional>
|
|
||||||
#include <queue>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class TimedQueue {
|
|
||||||
public:
|
|
||||||
TimedQueue() = default;
|
|
||||||
TimedQueue(const TimedQueue&) = delete;
|
|
||||||
TimedQueue(TimedQueue&&) = delete;
|
|
||||||
TimedQueue& operator=(const TimedQueue&) = delete;
|
|
||||||
TimedQueue& operator=(TimedQueue&&) = delete;
|
|
||||||
|
|
||||||
void Push(nf7::Env::Time time, T&& task) noexcept {
|
|
||||||
std::unique_lock<std::mutex> k(mtx_);
|
|
||||||
++n_;
|
|
||||||
q_.push(Item {.time = time, .index = index_++, .task = std::move(task)});
|
|
||||||
}
|
|
||||||
std::optional<T> Pop(nf7::Env::Time now = nf7::Env::Clock::now()) noexcept {
|
|
||||||
std::unique_lock<std::mutex> k(mtx_);
|
|
||||||
if (q_.empty() || q_.top().time > now) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
auto ret = std::move(q_.top());
|
|
||||||
q_.pop();
|
|
||||||
--n_;
|
|
||||||
k.unlock();
|
|
||||||
return ret.task;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<nf7::Env::Time> next() const noexcept {
|
|
||||||
std::unique_lock<std::mutex> k(mtx_);
|
|
||||||
return next_();
|
|
||||||
}
|
|
||||||
size_t size() const noexcept { return n_; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
mutable std::mutex mtx_;
|
|
||||||
|
|
||||||
std::optional<nf7::Env::Time> next_() const noexcept {
|
|
||||||
if (q_.empty()) return std::nullopt;
|
|
||||||
return q_.top().time;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct Item final {
|
|
||||||
nf7::Env::Time time;
|
|
||||||
size_t index;
|
|
||||||
T task;
|
|
||||||
};
|
|
||||||
struct Comp final {
|
|
||||||
bool operator()(const Item& a, const Item& b) noexcept {
|
|
||||||
return a.time != b.time? a.time > b.time: a.index > b.index;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::atomic<size_t> n_;
|
|
||||||
size_t index_ = 0;
|
|
||||||
|
|
||||||
std::priority_queue<Item, std::vector<Item>, Comp> q_;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class TimedWaitQueue final : private TimedQueue<T> {
|
|
||||||
public:
|
|
||||||
TimedWaitQueue() = default;
|
|
||||||
TimedWaitQueue(const TimedWaitQueue&) = delete;
|
|
||||||
TimedWaitQueue(TimedWaitQueue&&) = delete;
|
|
||||||
TimedWaitQueue& operator=(const TimedWaitQueue&) = delete;
|
|
||||||
TimedWaitQueue& operator=(TimedWaitQueue&&) = delete;
|
|
||||||
|
|
||||||
void Push(nf7::Env::Time time, T&& task) noexcept {
|
|
||||||
TimedQueue<T>::Push(time, std::move(task));
|
|
||||||
cv_.notify_all();
|
|
||||||
}
|
|
||||||
using TimedQueue<T>::Pop;
|
|
||||||
|
|
||||||
void Notify() noexcept {
|
|
||||||
cv_.notify_all();
|
|
||||||
}
|
|
||||||
void Wait(const auto& dur) noexcept {
|
|
||||||
std::unique_lock<std::mutex> k(mtx_);
|
|
||||||
if (auto t = next_()) {
|
|
||||||
cv_.wait_until(k, *t);
|
|
||||||
} else {
|
|
||||||
cv_.wait_for(k, dur);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
using TimedQueue<T>::next;
|
|
||||||
using TimedQueue<T>::size;
|
|
||||||
|
|
||||||
private:
|
|
||||||
using TimedQueue<T>::mtx_;
|
|
||||||
using TimedQueue<T>::next_;
|
|
||||||
|
|
||||||
std::condition_variable cv_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,23 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::util {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
inline size_t Uniq(std::vector<T>& v) noexcept {
|
|
||||||
size_t n = 0;
|
|
||||||
for (auto itr = v.begin(); itr < v.end();) {
|
|
||||||
if (v.end() != std::find(itr+1, v.end(), *itr)) {
|
|
||||||
itr = v.erase(itr);
|
|
||||||
++n;
|
|
||||||
} else {
|
|
||||||
++itr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nf7::util
|
|
@ -1,78 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cctype>
|
|
||||||
#include <functional>
|
|
||||||
#include <optional>
|
|
||||||
#include <span>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7::util {
|
|
||||||
|
|
||||||
inline std::string_view Trim(
|
|
||||||
std::string_view str,
|
|
||||||
const std::function<bool(char)>& func = [](auto c) { return std::isspace(c); }) noexcept {
|
|
||||||
while (!str.empty() && func(str.front())) {
|
|
||||||
str.remove_prefix(1);
|
|
||||||
}
|
|
||||||
while (!str.empty() && func(str.back())) {
|
|
||||||
str.remove_suffix(1);
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::optional<std::string_view> IterateTerms(std::string_view str, char c, size_t& i) noexcept {
|
|
||||||
std::string_view ret;
|
|
||||||
while (ret.empty() && i < str.size()) {
|
|
||||||
auto j = str.find(c, i);
|
|
||||||
if (j == std::string::npos) j = str.size();
|
|
||||||
|
|
||||||
ret = str.substr(i, j-i);
|
|
||||||
i = j+1;
|
|
||||||
}
|
|
||||||
if (ret.empty()) return std::nullopt;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void SplitAndAppend(std::vector<std::string>& dst, std::string_view src, char c = '\n') noexcept {
|
|
||||||
size_t itr = 0;
|
|
||||||
while (auto term = IterateTerms(src, c, itr)) {
|
|
||||||
dst.emplace_back(*term);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inline void JoinAndAppend(std::string& dst, std::span<const std::string> src, char c = '\n') noexcept {
|
|
||||||
for (auto& str : src) {
|
|
||||||
dst += str;
|
|
||||||
dst += c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::optional<std::string_view> SplitAndValidate(
|
|
||||||
std::string_view v,
|
|
||||||
const std::function<bool(std::string_view)> validator, char c = '\n') noexcept {
|
|
||||||
size_t itr = 0;
|
|
||||||
while (auto term = IterateTerms(v, c, itr)) {
|
|
||||||
if (validator(*term)) {
|
|
||||||
return term;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
inline std::optional<std::string_view> SplitAndValidate(
|
|
||||||
std::string_view v,
|
|
||||||
const std::function<void(std::string_view)> validator, char c = '\n') noexcept {
|
|
||||||
size_t itr = 0;
|
|
||||||
while (auto term = IterateTerms(v, c, itr)) {
|
|
||||||
try {
|
|
||||||
validator(*term);
|
|
||||||
} catch (nf7::Exception&) {
|
|
||||||
return term;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nf7::util
|
|
301
common/value.hh
301
common/value.hh
@ -1,301 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <utility>
|
|
||||||
#include <variant>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <yas/serialize.hpp>
|
|
||||||
#include <yas/types/std/pair.hpp>
|
|
||||||
#include <yas/types/std/string.hpp>
|
|
||||||
#include <yas/types/std/string_view.hpp>
|
|
||||||
#include <yas/types/std/variant.hpp>
|
|
||||||
#include <yas/types/std/vector.hpp>
|
|
||||||
#include <yas/types/utility/usertype.hpp>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
#include "common/yas_nf7.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
class Value {
|
|
||||||
public:
|
|
||||||
class IncompatibleException : public nf7::Exception {
|
|
||||||
public:
|
|
||||||
using nf7::Exception::Exception;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Data;
|
|
||||||
using TuplePair = std::pair<std::string, nf7::Value>;
|
|
||||||
|
|
||||||
class Pulse { };
|
|
||||||
using Boolean = bool;
|
|
||||||
using Integer = int64_t;
|
|
||||||
using Scalar = double;
|
|
||||||
using String = std::string;
|
|
||||||
using Vector = std::shared_ptr<std::vector<uint8_t>>;
|
|
||||||
using Tuple = std::shared_ptr<std::vector<TuplePair>>;
|
|
||||||
using DataPtr = std::shared_ptr<Data>;
|
|
||||||
|
|
||||||
using ConstVector = std::shared_ptr<const std::vector<uint8_t>>;
|
|
||||||
using ConstTuple = std::shared_ptr<const std::vector<TuplePair>>;
|
|
||||||
|
|
||||||
Value() noexcept {
|
|
||||||
}
|
|
||||||
Value(const Value&) = default;
|
|
||||||
Value(Value&&) = default;
|
|
||||||
Value& operator=(const Value&) = default;
|
|
||||||
Value& operator=(Value&&) = default;
|
|
||||||
|
|
||||||
Value(Pulse v) noexcept : value_(v) { }
|
|
||||||
Value& operator=(Pulse v) noexcept { value_ = v; return *this; }
|
|
||||||
Value(Integer v) noexcept : value_(v) { }
|
|
||||||
Value& operator=(Integer v) noexcept { value_ = v; return *this; }
|
|
||||||
Value(Scalar v) noexcept : value_(v) { }
|
|
||||||
Value& operator=(Scalar v) noexcept { value_ = v; return *this; }
|
|
||||||
Value(Boolean v) noexcept : value_(v) { }
|
|
||||||
Value& operator=(Boolean v) noexcept { value_ = v; return *this; }
|
|
||||||
Value(std::string_view v) noexcept : value_(std::string {v}) { }
|
|
||||||
Value& operator=(std::string_view v) noexcept { value_ = std::string(v); return *this; }
|
|
||||||
Value(String&& v) noexcept : value_(std::move(v)) { }
|
|
||||||
Value& operator=(String&& v) noexcept { value_ = std::move(v); return *this; }
|
|
||||||
Value(const Vector& v) noexcept { value_ = v; }
|
|
||||||
Value& operator=(const Vector& v) noexcept { value_ = v; return *this; }
|
|
||||||
Value(Vector&& v) noexcept { value_ = std::move(v); }
|
|
||||||
Value& operator=(Vector&& v) noexcept { value_ = std::move(v); return *this; }
|
|
||||||
Value(const ConstVector& v) noexcept { value_ = std::const_pointer_cast<std::vector<uint8_t>>(v); }
|
|
||||||
Value& operator=(const ConstVector& v) noexcept { value_ = std::const_pointer_cast<std::vector<uint8_t>>(v); return *this; }
|
|
||||||
Value(std::vector<uint8_t>&& v) noexcept { value_ = std::make_shared<std::vector<uint8_t>>(std::move(v)); }
|
|
||||||
Value& operator=(std::vector<uint8_t>&& v) noexcept { value_ = std::make_shared<std::vector<uint8_t>>(std::move(v)); return *this; }
|
|
||||||
Value(const Tuple& v) noexcept : value_(v) { }
|
|
||||||
Value& operator=(const Tuple& v) noexcept { value_ = v; return *this; }
|
|
||||||
Value(Tuple&& v) noexcept : value_(std::move(v)) { }
|
|
||||||
Value& operator=(Tuple&& v) noexcept { value_ = std::move(v); return *this; }
|
|
||||||
Value(std::vector<TuplePair>&& p) noexcept { value_ = std::make_shared<std::vector<TuplePair>>(std::move(p)); }
|
|
||||||
Value& operator=(std::vector<TuplePair>&& p) noexcept { value_ = std::make_shared<std::vector<TuplePair>>(std::move(p)); return *this; }
|
|
||||||
Value(std::vector<nf7::Value>&& v) noexcept {
|
|
||||||
std::vector<TuplePair> pairs;
|
|
||||||
pairs.reserve(v.size());
|
|
||||||
std::transform(v.begin(), v.end(), std::back_inserter(pairs),
|
|
||||||
[](auto& x) { return TuplePair {"", std::move(x)}; });
|
|
||||||
value_ = std::make_shared<std::vector<TuplePair>>(std::move(pairs));
|
|
||||||
}
|
|
||||||
Value& operator=(std::vector<nf7::Value>&& v) noexcept {
|
|
||||||
std::vector<TuplePair> pairs;
|
|
||||||
pairs.reserve(v.size());
|
|
||||||
std::transform(v.begin(), v.end(), std::back_inserter(pairs),
|
|
||||||
[](auto& x) { return TuplePair {"", std::move(x)}; });
|
|
||||||
value_ = std::make_shared<std::vector<TuplePair>>(std::move(pairs));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
Value(const DataPtr& v) noexcept : value_(v) { }
|
|
||||||
Value& operator=(const DataPtr& v) noexcept { value_ = v; return *this; }
|
|
||||||
Value(DataPtr&& v) noexcept : value_(std::move(v)) { }
|
|
||||||
Value& operator=(DataPtr&& v) noexcept { value_ = std::move(v); return *this; }
|
|
||||||
|
|
||||||
auto Visit(auto visitor) const noexcept {
|
|
||||||
return std::visit(visitor, value_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isPulse() const noexcept { return std::holds_alternative<Pulse>(value_); }
|
|
||||||
bool isBoolean() const noexcept { return std::holds_alternative<Boolean>(value_); }
|
|
||||||
bool isInteger() const noexcept { return std::holds_alternative<Integer>(value_); }
|
|
||||||
bool isScalar() const noexcept { return std::holds_alternative<Scalar>(value_); }
|
|
||||||
bool isString() const noexcept { return std::holds_alternative<String>(value_); }
|
|
||||||
bool isVector() const noexcept { return std::holds_alternative<Vector>(value_); }
|
|
||||||
bool isTuple() const noexcept { return std::holds_alternative<Tuple>(value_); }
|
|
||||||
bool isData() const noexcept { return std::holds_alternative<DataPtr>(value_); }
|
|
||||||
|
|
||||||
Integer integer() const { return get<Integer>(); }
|
|
||||||
Boolean boolean() const { return get<Boolean>(); }
|
|
||||||
Scalar scalar() const { return get<Scalar>(); }
|
|
||||||
const String& string() const { return get<String>(); }
|
|
||||||
const ConstVector vector() const { return get<Vector>(); }
|
|
||||||
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:
|
|
||||||
throw IncompatibleException("tuple index overflow");
|
|
||||||
}
|
|
||||||
const Value& tuple(std::string_view name) const {
|
|
||||||
auto& tup = *tuple();
|
|
||||||
auto itr = std::find_if(tup.begin(), tup.end(),
|
|
||||||
[&name](auto& x) { return x.first == name; });
|
|
||||||
return itr < tup.end()? itr->second:
|
|
||||||
throw IncompatibleException("unknown tuple field: "+std::string {name});
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
std::shared_ptr<T> data() const {
|
|
||||||
if (auto ptr = std::dynamic_pointer_cast<T>(data())) return ptr;
|
|
||||||
throw IncompatibleException("data pointer downcast failure");
|
|
||||||
}
|
|
||||||
|
|
||||||
Integer& integer() { return get<Integer>(); }
|
|
||||||
Boolean& boolean() { return get<Boolean>(); }
|
|
||||||
Scalar& scalar() { return get<Scalar>(); }
|
|
||||||
String& string() { return get<String>(); }
|
|
||||||
|
|
||||||
Vector vectorUniq() { return getUniq<Vector>(); }
|
|
||||||
Tuple tupleUniq() { return getUniq<Tuple>(); }
|
|
||||||
|
|
||||||
const char* typeName() const noexcept {
|
|
||||||
struct Visitor final {
|
|
||||||
public:
|
|
||||||
auto operator()(Pulse) noexcept { return "pulse"; }
|
|
||||||
auto operator()(Boolean) noexcept { return "boolean"; }
|
|
||||||
auto operator()(Integer) noexcept { return "integer"; }
|
|
||||||
auto operator()(Scalar) noexcept { return "scalar"; }
|
|
||||||
auto operator()(String) noexcept { return "string"; }
|
|
||||||
auto operator()(Vector) noexcept { return "vector"; }
|
|
||||||
auto operator()(Tuple) noexcept { return "tuple"; }
|
|
||||||
auto operator()(DataPtr) noexcept { return "data"; }
|
|
||||||
};
|
|
||||||
return Visit(Visitor{});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Ar>
|
|
||||||
Ar& serialize(Ar& ar) noexcept {
|
|
||||||
ar & value_;
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::variant<Pulse, Boolean, Integer, Scalar, String, Vector, Tuple, DataPtr> value_;
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
const T& get() const
|
|
||||||
try {
|
|
||||||
return std::get<T>(value_);
|
|
||||||
} catch (std::bad_variant_access&) {
|
|
||||||
throw IncompatibleException(
|
|
||||||
std::string{"expected "}+typeid(T).name()+" but it's "+typeName());
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
T& get()
|
|
||||||
try {
|
|
||||||
return std::get<T>(value_);
|
|
||||||
} catch (std::bad_variant_access&) {
|
|
||||||
throw IncompatibleException(
|
|
||||||
std::string{"expected "}+typeid(T).name()+" but it's "+typeName());
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
T getUniq() {
|
|
||||||
auto v = std::move(get<T>());
|
|
||||||
if (v.use_count() == 1) {
|
|
||||||
return v;
|
|
||||||
} else {
|
|
||||||
return std::make_shared<typename T::element_type>(*v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Value::Data {
|
|
||||||
public:
|
|
||||||
Data() = default;
|
|
||||||
virtual ~Data() = default;
|
|
||||||
Data(const Data&) = default;
|
|
||||||
Data(Data&&) = default;
|
|
||||||
Data& operator=(const Data&) = default;
|
|
||||||
Data& operator=(Data&&) = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
||||||
|
|
||||||
|
|
||||||
namespace yas::detail {
|
|
||||||
|
|
||||||
template <size_t F>
|
|
||||||
struct serializer<
|
|
||||||
type_prop::not_a_fundamental,
|
|
||||||
ser_case::use_internal_serializer,
|
|
||||||
F,
|
|
||||||
nf7::Value::Pulse> {
|
|
||||||
public:
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& save(Archive& ar, const nf7::Value::Pulse&) {
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& load(Archive& ar, nf7::Value::Pulse&) {
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <size_t F>
|
|
||||||
struct serializer<
|
|
||||||
type_prop::not_a_fundamental,
|
|
||||||
ser_case::use_internal_serializer,
|
|
||||||
F,
|
|
||||||
nf7::Value::Vector> {
|
|
||||||
public:
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& save(Archive&, const nf7::Value::Vector&) {
|
|
||||||
throw nf7::Exception("cannot serialize Value::Vector");
|
|
||||||
}
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& load(Archive&, nf7::Value::Vector&) {
|
|
||||||
throw nf7::DeserializeException("cannot deserialize Value::Vector");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <size_t F>
|
|
||||||
struct serializer<
|
|
||||||
type_prop::not_a_fundamental,
|
|
||||||
ser_case::use_internal_serializer,
|
|
||||||
F,
|
|
||||||
nf7::Value::Tuple> {
|
|
||||||
public:
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& save(Archive& ar, const nf7::Value::Tuple& tup) {
|
|
||||||
ar(*tup);
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& load(Archive& ar, nf7::Value::Tuple& tup) {
|
|
||||||
ar(*tup);
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <size_t F>
|
|
||||||
struct serializer<
|
|
||||||
type_prop::not_a_fundamental,
|
|
||||||
ser_case::use_internal_serializer,
|
|
||||||
F,
|
|
||||||
nf7::Value::DataPtr> {
|
|
||||||
public:
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& save(Archive&, const nf7::Value::DataPtr&) {
|
|
||||||
throw nf7::Exception("cannot serialize Value::DataPtr");
|
|
||||||
}
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& load(Archive&, nf7::Value::DataPtr&) {
|
|
||||||
throw nf7::DeserializeException("cannot deserialize Value::DataPtr");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace yas::detail
|
|
@ -1,34 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
#include <magic_enum.hpp>
|
|
||||||
|
|
||||||
#include <yas/serialize.hpp>
|
|
||||||
#include <yas/types/std/string.hpp>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace nf7 {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct EnumSerializer {
|
|
||||||
public:
|
|
||||||
static auto& save(auto& ar, auto t) {
|
|
||||||
ar(magic_enum::enum_name(t));
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
static auto& load(auto& ar, auto& t) {
|
|
||||||
std::string v;
|
|
||||||
ar(v);
|
|
||||||
if (auto ot = magic_enum::enum_cast<T>(v)) {
|
|
||||||
t = *ot;
|
|
||||||
} else {
|
|
||||||
throw nf7::DeserializeException {"unknown enum: "+v};
|
|
||||||
}
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nf7
|
|
@ -1,28 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <imgui.h>
|
|
||||||
#include <yas/serialize.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
namespace yas::detail {
|
|
||||||
|
|
||||||
template <size_t F>
|
|
||||||
struct serializer<
|
|
||||||
type_prop::not_a_fundamental,
|
|
||||||
ser_case::use_internal_serializer,
|
|
||||||
F,
|
|
||||||
ImVec2> {
|
|
||||||
public:
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& save(Archive& ar, const ImVec2& v) {
|
|
||||||
ar(v.x, v.y);
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& load(Archive& ar, ImVec2& v) {
|
|
||||||
ar(v.x, v.y);
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace yas::detail
|
|
@ -1,31 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ImNodes.h>
|
|
||||||
#include <yas/serialize.hpp>
|
|
||||||
|
|
||||||
#include "common/yas_imgui.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace yas::detail {
|
|
||||||
|
|
||||||
template <size_t F>
|
|
||||||
struct serializer<
|
|
||||||
type_prop::not_a_fundamental,
|
|
||||||
ser_case::use_internal_serializer,
|
|
||||||
F,
|
|
||||||
ImNodes::CanvasState> {
|
|
||||||
public:
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& save(Archive& ar, const ImNodes::CanvasState& canvas) {
|
|
||||||
ar(canvas.Zoom, canvas.Offset);
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& load(Archive& ar, ImNodes::CanvasState& canvas) {
|
|
||||||
ar(canvas.Zoom, canvas.Offset);
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace yas::detail
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <yas/serialize.hpp>
|
|
||||||
#include <yas/types/std/string.hpp>
|
|
||||||
|
|
||||||
#include "nf7.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace yas::detail {
|
|
||||||
|
|
||||||
template <size_t F>
|
|
||||||
struct serializer<
|
|
||||||
type_prop::not_a_fundamental,
|
|
||||||
ser_case::use_internal_serializer,
|
|
||||||
F,
|
|
||||||
std::unique_ptr<nf7::File>> {
|
|
||||||
public:
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& save(Archive& ar, const std::unique_ptr<nf7::File>& f) {
|
|
||||||
ar(std::string {f->type().name()});
|
|
||||||
|
|
||||||
typename Archive::ChunkGuard guard {ar};
|
|
||||||
f->Serialize(ar);
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& load(Archive& ar, std::unique_ptr<nf7::File>& f) {
|
|
||||||
std::string name;
|
|
||||||
ar(name);
|
|
||||||
auto& type = nf7::File::registry(name);
|
|
||||||
|
|
||||||
try {
|
|
||||||
typename Archive::ChunkGuard guard {ar};
|
|
||||||
f = type.Deserialize(ar);
|
|
||||||
guard.ValidateEnd();
|
|
||||||
} catch (...) {
|
|
||||||
f = nullptr;
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <size_t F>
|
|
||||||
struct serializer<
|
|
||||||
type_prop::not_a_fundamental,
|
|
||||||
ser_case::use_internal_serializer,
|
|
||||||
F,
|
|
||||||
std::shared_ptr<nf7::File>> {
|
|
||||||
public:
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& save(Archive& ar, const std::shared_ptr<nf7::File>& f) {
|
|
||||||
std::unique_ptr<nf7::File> uf(f.get());
|
|
||||||
ar(uf);
|
|
||||||
uf.release();
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& load(Archive& ar, std::shared_ptr<nf7::File>& f) {
|
|
||||||
std::unique_ptr<nf7::File> uf;
|
|
||||||
ar(uf);
|
|
||||||
f = std::move(uf);
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <size_t F>
|
|
||||||
struct serializer<
|
|
||||||
type_prop::not_a_fundamental,
|
|
||||||
ser_case::use_internal_serializer,
|
|
||||||
F,
|
|
||||||
nf7::File::Path> {
|
|
||||||
public:
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& save(Archive& ar, const nf7::File::Path& p) {
|
|
||||||
p.Serialize(ar);
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& load(Archive& ar, nf7::File::Path& p) {
|
|
||||||
p = {ar};
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace detail
|
|
@ -1,31 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
#include <yas/serialize.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
namespace yas::detail {
|
|
||||||
|
|
||||||
template <size_t F, typename T>
|
|
||||||
struct serializer<
|
|
||||||
type_prop::not_a_fundamental,
|
|
||||||
ser_case::use_internal_serializer,
|
|
||||||
F,
|
|
||||||
std::atomic<T>> {
|
|
||||||
public:
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& save(Archive& ar, const std::atomic<T>& v) {
|
|
||||||
ar(v.load());
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& load(Archive& ar, std::atomic<T>& v) {
|
|
||||||
T temp;
|
|
||||||
ar(temp);
|
|
||||||
v.store(temp);
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace yas::detail
|
|
@ -1,33 +0,0 @@
|
|||||||
#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
|
|
@ -1,25 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <yas/serialize.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
namespace yas::detail {
|
|
||||||
|
|
||||||
template <size_t F>
|
|
||||||
struct serializer<
|
|
||||||
type_prop::not_a_fundamental,
|
|
||||||
ser_case::use_internal_serializer,
|
|
||||||
F,
|
|
||||||
std::monostate> {
|
|
||||||
public:
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& save(Archive& ar, const std::monostate&) {
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
template <typename Archive>
|
|
||||||
static Archive& load(Archive& ar, std::monostate&) {
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace yas::detail
|
|
30
core/CMakeLists.txt
Normal file
30
core/CMakeLists.txt
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# ---- basic modules
|
||||||
|
set(MODS
|
||||||
|
# ---- interface layer
|
||||||
|
exec
|
||||||
|
|
||||||
|
# ---- implementation layer
|
||||||
|
any
|
||||||
|
lua
|
||||||
|
null
|
||||||
|
sdl2
|
||||||
|
|
||||||
|
# ---- usecase layer
|
||||||
|
init
|
||||||
|
test
|
||||||
|
)
|
||||||
|
|
||||||
|
# ---- core library
|
||||||
|
add_library(nf7core)
|
||||||
|
target_link_libraries(nf7core PRIVATE nf7if)
|
||||||
|
|
||||||
|
foreach(name IN LISTS MODS)
|
||||||
|
add_subdirectory(${name})
|
||||||
|
target_link_libraries(nf7core PRIVATE nf7core_${name})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# ---- generate all.h
|
||||||
|
target_meta_source(nf7core
|
||||||
|
PRIVATE all.c.sh
|
||||||
|
ARGS ${MODS}
|
||||||
|
)
|
36
core/all.c.sh
Executable file
36
core/all.c.sh
Executable file
@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "#include \"core/all.h\""
|
||||||
|
echo
|
||||||
|
echo "#include <assert.h>"
|
||||||
|
echo
|
||||||
|
echo "#include \"util/log.h\""
|
||||||
|
echo
|
||||||
|
echo "const uint32_t NF7CORE_MAX_MODS = UINT32_C($#);"
|
||||||
|
echo
|
||||||
|
echo "uint32_t nf7core_new(struct nf7* nf7, struct nf7_mod** mods) {"
|
||||||
|
echo " assert(nullptr != nf7);"
|
||||||
|
echo " assert(nullptr != mods);"
|
||||||
|
echo
|
||||||
|
echo " nf7->mods.ptr = mods;"
|
||||||
|
echo " nf7->mods.n = 0;"
|
||||||
|
echo
|
||||||
|
for name in $@; do
|
||||||
|
echo " {"
|
||||||
|
echo " extern const struct nf7_mod_meta nf7core_${name};"
|
||||||
|
echo " extern struct nf7_mod* nf7core_${name}_new(const struct nf7*);"
|
||||||
|
echo " nf7util_log_debug(\"loading module: %s\", nf7core_${name}.name);"
|
||||||
|
echo " struct nf7_mod* mod = nf7core_${name}_new(nf7);"
|
||||||
|
echo " if (nullptr != mod) {"
|
||||||
|
echo " assert(nullptr != mod->nf7);"
|
||||||
|
echo " assert(nullptr != mod->meta);"
|
||||||
|
echo " nf7->mods.ptr[nf7->mods.n++] = mod;"
|
||||||
|
echo " nf7util_log_info(\"loaded module: %s\", nf7core_${name}.name);"
|
||||||
|
echo " } else {"
|
||||||
|
echo " nf7util_log_warn(\"failed to load module: %s\", nf7core_${name}.name);"
|
||||||
|
echo " }"
|
||||||
|
echo " }"
|
||||||
|
echo
|
||||||
|
done
|
||||||
|
echo " return nf7->mods.n;"
|
||||||
|
echo "}"
|
14
core/all.h
Normal file
14
core/all.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "nf7.h"
|
||||||
|
|
||||||
|
|
||||||
|
// A maximum number of modules which nf7core may provide.
|
||||||
|
extern const uint32_t NF7CORE_MAX_MODS;
|
||||||
|
|
||||||
|
|
||||||
|
// Initializes all modules and returns their instances.
|
||||||
|
// sizeof(mods)/sizeof(mods[0]) should be equal to NF7CORE_MAX_MODS
|
||||||
|
uint32_t nf7core_new(struct nf7* nf7, struct nf7_mod** mods);
|
15
core/any/CMakeLists.txt
Normal file
15
core/any/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
add_library(nf7core_any)
|
||||||
|
target_sources(nf7core_any
|
||||||
|
PRIVATE
|
||||||
|
idea.c
|
||||||
|
mod.c
|
||||||
|
)
|
||||||
|
target_link_libraries(nf7core_any
|
||||||
|
PRIVATE
|
||||||
|
nf7if
|
||||||
|
nf7util
|
||||||
|
|
||||||
|
PUBLIC
|
||||||
|
nf7core_exec
|
||||||
|
)
|
||||||
|
|
103
core/any/idea.c
Normal file
103
core/any/idea.c
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// No copyright
|
||||||
|
#include "core/any/idea.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "util/log.h"
|
||||||
|
#include "util/malloc.h"
|
||||||
|
|
||||||
|
#include "core/exec/entity.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct nf7core_any_entity {
|
||||||
|
struct nf7core_exec_entity super;
|
||||||
|
|
||||||
|
struct nf7util_malloc* malloc;
|
||||||
|
|
||||||
|
struct nf7core_exec_entity* entity;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nf7core_exec_entity* new_(struct nf7core_exec*);
|
||||||
|
void del_(struct nf7core_exec_entity*);
|
||||||
|
void send_(struct nf7core_exec_entity*, struct nf7util_buffer*);
|
||||||
|
|
||||||
|
|
||||||
|
struct nf7core_exec_entity* new_(struct nf7core_exec* exec) {
|
||||||
|
assert(nullptr != exec);
|
||||||
|
|
||||||
|
struct nf7core_any_entity* this = nf7util_malloc_alloc(exec->malloc, sizeof(*this));
|
||||||
|
if (nullptr == this) {
|
||||||
|
nf7util_log_error("failed to allocate new entity");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
*this = (struct nf7core_any_entity) {
|
||||||
|
.super = {
|
||||||
|
.idea = &nf7core_any_idea,
|
||||||
|
.mod = exec,
|
||||||
|
},
|
||||||
|
.malloc = exec->malloc,
|
||||||
|
};
|
||||||
|
return &this->super;
|
||||||
|
}
|
||||||
|
|
||||||
|
void del_(struct nf7core_exec_entity* entity) {
|
||||||
|
struct nf7core_any_entity* this = (void*) entity;
|
||||||
|
if (nullptr != this) {
|
||||||
|
nf7core_exec_entity_del(this->entity);
|
||||||
|
nf7util_malloc_free(this->malloc, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_(struct nf7core_exec_entity* entity, struct nf7util_buffer* buf) {
|
||||||
|
assert(nullptr != entity);
|
||||||
|
assert(nullptr != buf);
|
||||||
|
|
||||||
|
struct nf7core_any_entity* this = (void*) entity;
|
||||||
|
if (nullptr != this->entity) {
|
||||||
|
nf7core_exec_entity_send(this->entity, buf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get name of the requested idea
|
||||||
|
const uint8_t* name = buf->array.ptr;
|
||||||
|
const uint64_t namelen = buf->array.n;
|
||||||
|
if (0U == namelen) {
|
||||||
|
nf7util_log_warn("expected an idea name, but got an empty string");
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the requested idea and create new entity
|
||||||
|
this->entity = nf7core_exec_entity_new(this->super.mod, name, namelen);
|
||||||
|
|
||||||
|
// assign the return value
|
||||||
|
struct nf7util_buffer* result = nullptr;
|
||||||
|
if (nullptr != this->entity) {
|
||||||
|
result = nf7util_buffer_new_from_cstr(this->malloc, "");
|
||||||
|
nf7util_log_debug("sub-entity is created: %.*s", (int) namelen, name);
|
||||||
|
} else {
|
||||||
|
result = nf7util_buffer_new_from_cstr(this->malloc, "FAIL");
|
||||||
|
nf7util_log_warn("unknown idea requested: %.*s", (int) namelen, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the result
|
||||||
|
if (nullptr == result) {
|
||||||
|
nf7util_log_error("failed to allocate a buffer to return result");
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
nf7core_exec_entity_recv(&this->super, result);
|
||||||
|
|
||||||
|
EXIT:
|
||||||
|
nf7util_buffer_unref(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const struct nf7core_exec_idea nf7core_any_idea = {
|
||||||
|
.name = (const uint8_t*) "nf7core_any",
|
||||||
|
.details = (const uint8_t*) "creates and wraps other entity of an idea chosen at runtime",
|
||||||
|
.mod = &nf7core_any,
|
||||||
|
|
||||||
|
.new = new_,
|
||||||
|
.del = del_,
|
||||||
|
.send = send_,
|
||||||
|
};
|
7
core/any/idea.h
Normal file
7
core/any/idea.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// No copyright
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/any/mod.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern const struct nf7core_exec_idea nf7core_any_idea;
|
66
core/any/mod.c
Normal file
66
core/any/mod.c
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// No copyright
|
||||||
|
#include "core/any/mod.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
|
#include "core/any/idea.h"
|
||||||
|
#include "core/exec/idea.h"
|
||||||
|
|
||||||
|
|
||||||
|
static void del_(struct nf7_mod*);
|
||||||
|
|
||||||
|
|
||||||
|
struct nf7_mod* nf7core_any_new(struct nf7* nf7) {
|
||||||
|
assert(nullptr != nf7);
|
||||||
|
|
||||||
|
// find nf7core_exec
|
||||||
|
struct nf7core_exec* exec = (void*) nf7_get_mod_by_meta(nf7, &nf7core_exec);
|
||||||
|
if (nullptr == exec) {
|
||||||
|
nf7util_log_error("not found nf7core_exec, nf7core_any is disabled");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create module data
|
||||||
|
struct nf7core_any* this = nf7util_malloc_alloc(nf7->malloc, sizeof(*this));
|
||||||
|
if (nullptr == this) {
|
||||||
|
nf7util_log_error("failed to allocate module context");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
*this = (struct nf7core_any) {
|
||||||
|
.super = {
|
||||||
|
.nf7 = nf7,
|
||||||
|
.meta = &nf7core_any,
|
||||||
|
},
|
||||||
|
.malloc = nf7->malloc,
|
||||||
|
.exec = exec,
|
||||||
|
};
|
||||||
|
|
||||||
|
// register idea
|
||||||
|
if (!nf7core_exec_idea_register(exec, &nf7core_any_idea)) {
|
||||||
|
nf7util_log_error("failed to register an idea, nf7core_any");
|
||||||
|
goto ABORT;
|
||||||
|
}
|
||||||
|
return &this->super;
|
||||||
|
|
||||||
|
ABORT:
|
||||||
|
del_(&this->super);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void del_(struct nf7_mod* mod) {
|
||||||
|
struct nf7core_any* this = (void*) mod;
|
||||||
|
if (nullptr != this) {
|
||||||
|
nf7util_malloc_free(this->malloc, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct nf7_mod_meta nf7core_any = {
|
||||||
|
.name = (const uint8_t*) "nf7core_any",
|
||||||
|
.desc = (const uint8_t*) "executes any things",
|
||||||
|
.ver = NF7_VERSION,
|
||||||
|
|
||||||
|
.del = del_,
|
||||||
|
};
|
28
core/any/mod.h
Normal file
28
core/any/mod.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// No copyright
|
||||||
|
//
|
||||||
|
// This module provides an idea, nf7core_any. Its entity can create and wrap
|
||||||
|
// other entity of any idea chosen at runtime.
|
||||||
|
//
|
||||||
|
// An entity of idea, nf7core_any, is composed of 2 states:
|
||||||
|
// INIT state:
|
||||||
|
// The entity is in this state right after the born. Accepts a string that
|
||||||
|
// expressing idea name and returns a string. If the returned string is
|
||||||
|
// empty, transitions to PIPE state with an sub-entity of the specified idea,
|
||||||
|
// otherwise error.
|
||||||
|
// PIPE state:
|
||||||
|
// All buffers from the client is passed to the sub-entity, and from the
|
||||||
|
// sub-entity is to the cleint.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "nf7.h"
|
||||||
|
|
||||||
|
#include "core/exec/mod.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct nf7core_any {
|
||||||
|
struct nf7_mod super;
|
||||||
|
|
||||||
|
struct nf7util_malloc* malloc;
|
||||||
|
struct nf7core_exec* exec;
|
||||||
|
};
|
||||||
|
extern const struct nf7_mod_meta nf7core_any;
|
14
core/exec/CMakeLists.txt
Normal file
14
core/exec/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
add_library(nf7core_exec)
|
||||||
|
target_sources(nf7core_exec
|
||||||
|
PRIVATE
|
||||||
|
mod.c
|
||||||
|
PUBLIC
|
||||||
|
entity.h
|
||||||
|
idea.h
|
||||||
|
mod.h
|
||||||
|
)
|
||||||
|
target_link_libraries(nf7core_exec
|
||||||
|
PRIVATE
|
||||||
|
nf7if
|
||||||
|
nf7util
|
||||||
|
)
|
74
core/exec/entity.h
Normal file
74
core/exec/entity.h
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// No copyright
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
|
#include "core/exec/idea.h"
|
||||||
|
#include "core/exec/mod.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct nf7core_exec_entity {
|
||||||
|
const struct nf7core_exec_idea* idea;
|
||||||
|
struct nf7core_exec* mod;
|
||||||
|
|
||||||
|
void* data;
|
||||||
|
void (*on_recv)(
|
||||||
|
struct nf7core_exec_entity*,
|
||||||
|
struct nf7util_buffer*);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static inline struct nf7core_exec_entity* nf7core_exec_entity_new(
|
||||||
|
struct nf7core_exec* mod, const uint8_t* name, size_t namelen) {
|
||||||
|
const struct nf7core_exec_idea* idea =
|
||||||
|
nf7core_exec_idea_find(mod, name, namelen);
|
||||||
|
if (nullptr == idea) {
|
||||||
|
nf7util_log_error("missing idea: %.*s", (int) namelen, name);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct nf7core_exec_entity* entity = idea->new(mod);
|
||||||
|
if (nullptr == entity) {
|
||||||
|
nf7util_log_error("failed to create entity of '%.*s'", (int) namelen, name);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
assert(idea == entity->idea);
|
||||||
|
assert(mod == entity->mod);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The implementation of entity use this to send buffer to the client.
|
||||||
|
// Takes ownership of buf.
|
||||||
|
static inline void nf7core_exec_entity_recv(struct nf7core_exec_entity* this, struct nf7util_buffer* buf) {
|
||||||
|
assert(nullptr != this);
|
||||||
|
assert(nullptr != buf);
|
||||||
|
|
||||||
|
if (nullptr != this->on_recv) {
|
||||||
|
this->on_recv(this, buf);
|
||||||
|
} else {
|
||||||
|
nf7util_buffer_unref(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The client of entity use this to send buffer to the implementation.
|
||||||
|
// Takes ownership of buf.
|
||||||
|
static inline void nf7core_exec_entity_send(
|
||||||
|
struct nf7core_exec_entity* this, struct nf7util_buffer* buf) {
|
||||||
|
assert(nullptr != this);
|
||||||
|
assert(nullptr != this->idea);
|
||||||
|
assert(nullptr != this->idea->send);
|
||||||
|
assert(nullptr != buf);
|
||||||
|
|
||||||
|
this->idea->send(this, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void nf7core_exec_entity_del(struct nf7core_exec_entity* this) {
|
||||||
|
if (nullptr != this) {
|
||||||
|
assert(nullptr != this->idea);
|
||||||
|
assert(nullptr != this->idea->del);
|
||||||
|
|
||||||
|
this->idea->del(this);
|
||||||
|
}
|
||||||
|
}
|
48
core/exec/idea.h
Normal file
48
core/exec/idea.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// No copyright
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "util/buffer.h"
|
||||||
|
#include "util/str.h"
|
||||||
|
|
||||||
|
#include "core/exec/mod.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct nf7core_exec_idea {
|
||||||
|
const uint8_t* name;
|
||||||
|
const uint8_t* details;
|
||||||
|
const struct nf7_mod_meta* mod;
|
||||||
|
|
||||||
|
struct nf7core_exec_entity* (*new)(struct nf7core_exec*);
|
||||||
|
void (*del)(struct nf7core_exec_entity*);
|
||||||
|
|
||||||
|
void (*send)(
|
||||||
|
struct nf7core_exec_entity*,
|
||||||
|
struct nf7util_buffer*);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static inline bool nf7core_exec_idea_register(
|
||||||
|
struct nf7core_exec* mod, const struct nf7core_exec_idea* idea) {
|
||||||
|
assert(nullptr != mod);
|
||||||
|
assert(nullptr != idea);
|
||||||
|
|
||||||
|
return nf7core_exec_ideas_insert(&mod->ideas, UINT64_MAX, idea);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const struct nf7core_exec_idea* nf7core_exec_idea_find(
|
||||||
|
const struct nf7core_exec* mod, const uint8_t* name, size_t namelen) {
|
||||||
|
assert(nullptr != mod);
|
||||||
|
assert(nullptr != name || 0U == namelen);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < mod->ideas.n; ++i) {
|
||||||
|
const struct nf7core_exec_idea* idea = mod->ideas.ptr[i];
|
||||||
|
if (nf7util_str_equal_cstr(name, namelen, idea->name)) {
|
||||||
|
return idea;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
50
core/exec/mod.c
Normal file
50
core/exec/mod.c
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// No copyright
|
||||||
|
#include "core/exec/mod.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
|
|
||||||
|
static void del_(struct nf7_mod*);
|
||||||
|
|
||||||
|
|
||||||
|
struct nf7_mod* nf7core_exec_new(struct nf7* nf7) {
|
||||||
|
assert(nullptr != nf7);
|
||||||
|
|
||||||
|
struct nf7core_exec* this = nf7util_malloc_alloc(nf7->malloc, sizeof(*this));
|
||||||
|
if (nullptr == this) {
|
||||||
|
nf7util_log_error("failed to allocate module context");
|
||||||
|
goto ABORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
*this = (struct nf7core_exec) {
|
||||||
|
.super = {
|
||||||
|
.nf7 = nf7,
|
||||||
|
.meta = &nf7core_exec,
|
||||||
|
},
|
||||||
|
.malloc = nf7->malloc,
|
||||||
|
};
|
||||||
|
|
||||||
|
nf7core_exec_ideas_init(&this->ideas, this->malloc);
|
||||||
|
return &this->super;
|
||||||
|
|
||||||
|
ABORT:
|
||||||
|
nf7util_log_warn("aborting module init");
|
||||||
|
del_((struct nf7_mod*) this);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void del_(struct nf7_mod* mod) {
|
||||||
|
struct nf7core_exec* this = (void*) mod;
|
||||||
|
nf7core_exec_ideas_deinit(&this->ideas);
|
||||||
|
nf7util_malloc_free(this->malloc, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct nf7_mod_meta nf7core_exec = {
|
||||||
|
.name = (const uint8_t*) "nf7core_exec",
|
||||||
|
.desc = (const uint8_t*) "provides a registry for executables",
|
||||||
|
.ver = NF7_VERSION,
|
||||||
|
|
||||||
|
.del = del_,
|
||||||
|
};
|
25
core/exec/mod.h
Normal file
25
core/exec/mod.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// No copyright
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "nf7.h"
|
||||||
|
|
||||||
|
#include "util/array.h"
|
||||||
|
#include "util/malloc.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct nf7core_exec_idea;
|
||||||
|
struct nf7core_exec_entity;
|
||||||
|
|
||||||
|
NF7UTIL_ARRAY_INLINE(nf7core_exec_ideas, const struct nf7core_exec_idea*);
|
||||||
|
|
||||||
|
|
||||||
|
struct nf7core_exec {
|
||||||
|
struct nf7_mod super;
|
||||||
|
|
||||||
|
struct nf7* nf7;
|
||||||
|
struct nf7util_malloc* malloc;
|
||||||
|
|
||||||
|
struct nf7core_exec_ideas ideas;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const struct nf7_mod_meta nf7core_exec;
|
12
core/init/CMakeLists.txt
Normal file
12
core/init/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
add_library(nf7core_init)
|
||||||
|
target_sources(nf7core_init
|
||||||
|
PRIVATE
|
||||||
|
mod.c
|
||||||
|
factory.priv.h
|
||||||
|
)
|
||||||
|
target_link_libraries(nf7core_init
|
||||||
|
PRIVATE
|
||||||
|
nf7if
|
||||||
|
nf7util
|
||||||
|
)
|
||||||
|
|
95
core/init/factory.priv.h
Normal file
95
core/init/factory.priv.h
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <uv.h>
|
||||||
|
|
||||||
|
#include "nf7.h"
|
||||||
|
|
||||||
|
#include "core/exec/entity.h"
|
||||||
|
#include "core/exec/mod.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct nf7core_init_factory {
|
||||||
|
struct nf7* nf7;
|
||||||
|
|
||||||
|
struct nf7util_malloc* malloc;
|
||||||
|
uv_loop_t* uv;
|
||||||
|
uv_idle_t idle;
|
||||||
|
|
||||||
|
const uint8_t* idea_name;
|
||||||
|
size_t idea_namelen;
|
||||||
|
|
||||||
|
void* data;
|
||||||
|
void (*on_created)(struct nf7core_init_factory*, struct nf7core_exec_entity*);
|
||||||
|
// after the callback is called
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct nf7core_init_factory* factory_new_(struct nf7*, const uint8_t*, size_t);
|
||||||
|
static void factory_del_(struct nf7core_init_factory*);
|
||||||
|
static void factory_on_idle_(uv_idle_t*);
|
||||||
|
static void factory_on_close_(uv_handle_t*);
|
||||||
|
|
||||||
|
|
||||||
|
static inline struct nf7core_init_factory* factory_new_(
|
||||||
|
struct nf7* nf7, const uint8_t* idea_name, size_t idea_namelen) {
|
||||||
|
struct nf7core_init_factory* this = nf7util_malloc_alloc(nf7->malloc, sizeof(*this));
|
||||||
|
if (nullptr == this) {
|
||||||
|
nf7util_log_error("failed to allocate the first factory");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
*this = (struct nf7core_init_factory) {
|
||||||
|
.nf7 = nf7,
|
||||||
|
.malloc = nf7->malloc,
|
||||||
|
.uv = nf7->uv,
|
||||||
|
.idea_name = idea_name,
|
||||||
|
.idea_namelen = idea_namelen,
|
||||||
|
};
|
||||||
|
|
||||||
|
uv_idle_init(this->uv, &this->idle);
|
||||||
|
this->idle.data = this;
|
||||||
|
|
||||||
|
uv_idle_start(&this->idle, factory_on_idle_);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void factory_del_(struct nf7core_init_factory* this) {
|
||||||
|
if (nullptr == this) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (nullptr != this->idle.data) {
|
||||||
|
uv_idle_stop(&this->idle);
|
||||||
|
uv_close((uv_handle_t*) &this->idle, factory_on_close_);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nf7util_malloc_free(this->malloc, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void factory_on_idle_(uv_idle_t* idle) {
|
||||||
|
assert(nullptr != idle);
|
||||||
|
struct nf7core_init_factory* this = idle->data;
|
||||||
|
|
||||||
|
struct nf7core_exec* exec = (void*) nf7_get_mod_by_meta(this->nf7, &nf7core_exec);
|
||||||
|
if (nullptr == exec) {
|
||||||
|
nf7util_log_error("nf7core_exec module is not installed");
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct nf7core_exec_entity* entity = nf7core_exec_entity_new(exec, this->idea_name, this->idea_namelen);
|
||||||
|
if (nullptr == entity) {
|
||||||
|
nf7util_log_error("failed to create new entity");
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(nullptr != this->on_created);
|
||||||
|
this->on_created(this, entity);
|
||||||
|
|
||||||
|
EXIT:
|
||||||
|
uv_idle_stop(&this->idle);
|
||||||
|
}
|
||||||
|
static inline void factory_on_close_(uv_handle_t* handle) {
|
||||||
|
assert(nullptr != handle);
|
||||||
|
struct nf7core_init_factory* this = handle->data;
|
||||||
|
handle->data = nullptr;
|
||||||
|
factory_del_(this);
|
||||||
|
}
|
97
core/init/mod.c
Normal file
97
core/init/mod.c
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
// No copyright
|
||||||
|
#include "core/init/mod.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <uv.h>
|
||||||
|
|
||||||
|
#include "nf7.h"
|
||||||
|
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
|
#include "core/exec/mod.h"
|
||||||
|
#include "core/init/factory.priv.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define IDEA_NAME "nf7core_any"
|
||||||
|
|
||||||
|
|
||||||
|
static void del_(struct nf7_mod*);
|
||||||
|
static void start_(struct nf7core_init_factory*, struct nf7core_exec_entity*);
|
||||||
|
|
||||||
|
struct nf7_mod* nf7core_init_new(struct nf7* nf7) {
|
||||||
|
assert(nullptr != nf7);
|
||||||
|
|
||||||
|
struct nf7core_init* this = nf7util_malloc_alloc(nf7->malloc, sizeof(*this));
|
||||||
|
if (nullptr == this) {
|
||||||
|
nf7util_log_error("failed to allocate module context");
|
||||||
|
goto ABORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
*this = (struct nf7core_init) {
|
||||||
|
.super = {
|
||||||
|
.meta = &nf7core_init,
|
||||||
|
.nf7 = nf7,
|
||||||
|
},
|
||||||
|
.malloc = nf7->malloc,
|
||||||
|
.uv = nf7->uv,
|
||||||
|
};
|
||||||
|
|
||||||
|
this->factory = factory_new_(nf7, (const uint8_t*) IDEA_NAME, sizeof(IDEA_NAME)-1);
|
||||||
|
if (nullptr == this->factory) {
|
||||||
|
nf7util_log_error("failed to start the first factory");
|
||||||
|
goto ABORT;
|
||||||
|
}
|
||||||
|
this->factory->data = this;
|
||||||
|
this->factory->on_created = start_;
|
||||||
|
|
||||||
|
return &this->super;
|
||||||
|
|
||||||
|
ABORT:
|
||||||
|
del_(&this->super);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void del_(struct nf7_mod* mod) {
|
||||||
|
struct nf7core_init* this = (void*) mod;
|
||||||
|
if (nullptr == this) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nf7util_log_info("delete factory");
|
||||||
|
factory_del_(this->factory);
|
||||||
|
this->factory = nullptr;
|
||||||
|
nf7core_exec_entity_del(this->entity);
|
||||||
|
nf7util_malloc_free(this->malloc, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void start_(struct nf7core_init_factory* factory, struct nf7core_exec_entity* entity) {
|
||||||
|
assert(nullptr != factory);
|
||||||
|
struct nf7core_init* this = factory->data;
|
||||||
|
assert(nullptr != this);
|
||||||
|
|
||||||
|
this->entity = entity;
|
||||||
|
if (nullptr == this->entity) {
|
||||||
|
nf7util_log_warn("failed to create new entity");
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct nf7util_buffer* buf = nf7util_buffer_new(this->malloc, 0);
|
||||||
|
if (nullptr == buf) {
|
||||||
|
nf7util_log_error("failed to allocate an empty buffer to send as the first trigger");
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
nf7core_exec_entity_send(this->entity, buf);
|
||||||
|
|
||||||
|
EXIT:
|
||||||
|
this->factory = nullptr;
|
||||||
|
factory_del_(factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct nf7_mod_meta nf7core_init = {
|
||||||
|
.name = (const uint8_t*) "nf7core_init",
|
||||||
|
.desc = (const uint8_t*) "creates the first entity",
|
||||||
|
.ver = NF7_VERSION,
|
||||||
|
|
||||||
|
.del = del_,
|
||||||
|
};
|
22
core/init/mod.h
Normal file
22
core/init/mod.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// No copyright
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <uv.h>
|
||||||
|
|
||||||
|
#include "nf7.h"
|
||||||
|
|
||||||
|
#include "core/exec/entity.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct nf7core_init_factory;
|
||||||
|
|
||||||
|
struct nf7core_init {
|
||||||
|
struct nf7_mod super;
|
||||||
|
|
||||||
|
struct nf7util_malloc* malloc;
|
||||||
|
uv_loop_t* uv;
|
||||||
|
|
||||||
|
struct nf7core_init_factory* factory;
|
||||||
|
struct nf7core_exec_entity* entity;
|
||||||
|
};
|
||||||
|
extern const struct nf7_mod_meta nf7core_init;
|
21
core/lua/CMakeLists.txt
Normal file
21
core/lua/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
add_library(nf7core_lua)
|
||||||
|
target_sources(nf7core_lua
|
||||||
|
PRIVATE
|
||||||
|
mod.c
|
||||||
|
thread.c
|
||||||
|
value_ptr.c
|
||||||
|
PUBLIC
|
||||||
|
mod.h
|
||||||
|
thread.h
|
||||||
|
value.h
|
||||||
|
value_ptr.h
|
||||||
|
)
|
||||||
|
target_link_libraries(nf7core_lua
|
||||||
|
PRIVATE
|
||||||
|
nf7if
|
||||||
|
nf7util
|
||||||
|
luajit
|
||||||
|
)
|
||||||
|
target_tests(nf7core_lua
|
||||||
|
thread.test.c
|
||||||
|
)
|
66
core/lua/mod.c
Normal file
66
core/lua/mod.c
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// No copyright
|
||||||
|
#include "core/lua/mod.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
|
#include "core/lua/thread.h"
|
||||||
|
|
||||||
|
|
||||||
|
static void del_(struct nf7core_lua*);
|
||||||
|
|
||||||
|
struct nf7_mod* nf7core_lua_new(struct nf7* nf7) {
|
||||||
|
assert(nullptr != nf7);
|
||||||
|
|
||||||
|
struct nf7core_lua* this =
|
||||||
|
nf7util_malloc_alloc(nf7->malloc, sizeof(*this));
|
||||||
|
if (nullptr == this) {
|
||||||
|
nf7util_log_error("failed to allocate a module context");
|
||||||
|
goto ABORT;
|
||||||
|
}
|
||||||
|
*this = (struct nf7core_lua) {
|
||||||
|
.super = {
|
||||||
|
.nf7 = nf7,
|
||||||
|
.meta = &nf7core_lua,
|
||||||
|
},
|
||||||
|
.malloc = nf7->malloc,
|
||||||
|
.uv = nf7->uv,
|
||||||
|
};
|
||||||
|
|
||||||
|
this->thread = nf7core_lua_thread_new(this, nullptr, nullptr);
|
||||||
|
if (nullptr == this->thread) {
|
||||||
|
nf7util_log_error("failed to create main thread");
|
||||||
|
goto ABORT;
|
||||||
|
}
|
||||||
|
return &this->super;
|
||||||
|
|
||||||
|
ABORT:
|
||||||
|
nf7util_log_warn("aborting lua module init");
|
||||||
|
del_(this);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void del_(struct nf7core_lua* this) {
|
||||||
|
if (nullptr == this) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nullptr != this->thread) {
|
||||||
|
nf7core_lua_thread_unref(this->thread);
|
||||||
|
}
|
||||||
|
nf7util_malloc_free(this->malloc, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void del_mod_(struct nf7_mod* mod) {
|
||||||
|
struct nf7core_lua* this = (void*) mod;
|
||||||
|
del_(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct nf7_mod_meta nf7core_lua = {
|
||||||
|
.name = (const uint8_t*) "nf7core_lua",
|
||||||
|
.desc = (const uint8_t*) "lua script execution",
|
||||||
|
.ver = NF7_VERSION,
|
||||||
|
|
||||||
|
.del = del_mod_,
|
||||||
|
};
|
25
core/lua/mod.h
Normal file
25
core/lua/mod.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// No copyright
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
|
||||||
|
#include "nf7.h"
|
||||||
|
|
||||||
|
#include "util/malloc.h"
|
||||||
|
#include "util/refcnt.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern const struct nf7_mod_meta nf7core_lua;
|
||||||
|
|
||||||
|
struct nf7core_lua {
|
||||||
|
struct nf7_mod super;
|
||||||
|
|
||||||
|
struct nf7util_malloc* malloc;
|
||||||
|
uv_loop_t* uv;
|
||||||
|
|
||||||
|
struct nf7core_lua_thread* thread;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nf7_mod* nf7core_lua_new(struct nf7*);
|
180
core/lua/thread.c
Normal file
180
core/lua/thread.c
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
// No copyright
|
||||||
|
#include "core/lua/thread.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
|
|
||||||
|
static void* alloc_(void*, void*, size_t, size_t);
|
||||||
|
static void del_(struct nf7core_lua_thread*);
|
||||||
|
|
||||||
|
static void on_time_(uv_timer_t*);
|
||||||
|
static void on_close_(uv_handle_t*);
|
||||||
|
|
||||||
|
NF7UTIL_REFCNT_IMPL(, nf7core_lua_thread, {del_(this);});
|
||||||
|
|
||||||
|
struct nf7core_lua_thread* nf7core_lua_thread_new(
|
||||||
|
struct nf7core_lua* mod,
|
||||||
|
struct nf7core_lua_thread* base,
|
||||||
|
struct nf7core_lua_value_ptr* func) {
|
||||||
|
assert(nullptr != mod);
|
||||||
|
|
||||||
|
struct nf7core_lua_thread* this =
|
||||||
|
nf7util_malloc_alloc(mod->malloc, sizeof(*this));
|
||||||
|
if (nullptr == this) {
|
||||||
|
nf7util_log_error("failed to allocate new thread context");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
*this = (struct nf7core_lua_thread) {
|
||||||
|
.mod = mod,
|
||||||
|
.malloc = mod->malloc,
|
||||||
|
.uv = mod->uv,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (0 != nf7util_log_uv(uv_timer_init(this->uv, &this->timer))) {
|
||||||
|
nf7util_log_error("failed to init uv timer");
|
||||||
|
goto ABORT;
|
||||||
|
}
|
||||||
|
this->timer.data = this;
|
||||||
|
|
||||||
|
if (nullptr != base) {
|
||||||
|
this->lua = lua_newthread(base->lua);
|
||||||
|
if (nullptr == this->lua) {
|
||||||
|
nf7util_log_error("failed to allocate new lua thread");
|
||||||
|
goto ABORT;
|
||||||
|
}
|
||||||
|
this->base = base;
|
||||||
|
nf7core_lua_thread_ref(this->base);
|
||||||
|
} else {
|
||||||
|
this->lua_owned = true;
|
||||||
|
this->lua = lua_newstate(alloc_, this);
|
||||||
|
if (nullptr == this->lua) {
|
||||||
|
nf7util_log_error("failed to allocate new lua state");
|
||||||
|
goto ABORT;
|
||||||
|
}
|
||||||
|
nf7util_log_debug("new lua state is created");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nullptr != func) {
|
||||||
|
this->state = NF7CORE_LUA_THREAD_PAUSED;
|
||||||
|
nf7core_lua_value_ptr_push(func, this->lua);
|
||||||
|
} else {
|
||||||
|
this->state = NF7CORE_LUA_THREAD_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
nf7core_lua_thread_ref(this);
|
||||||
|
return this;
|
||||||
|
|
||||||
|
ABORT:
|
||||||
|
nf7util_log_warn("aborting thread creation");
|
||||||
|
del_(this);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nf7core_lua_thread_resume_varg_after(
|
||||||
|
struct nf7core_lua_thread* this, uint64_t timeout, va_list vargs) {
|
||||||
|
assert(nullptr != this);
|
||||||
|
assert(NF7CORE_LUA_THREAD_PAUSED == this->state);
|
||||||
|
|
||||||
|
// stores parameters
|
||||||
|
for (uint32_t i = 0;; ++i) {
|
||||||
|
struct nf7core_lua_value* src = va_arg(vargs, struct nf7core_lua_value*);
|
||||||
|
if (nullptr == src) {
|
||||||
|
this->args.n = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
assert(i < NF7CORE_LUA_THREAD_MAX_ARGS &&
|
||||||
|
"too many args or forgotten last nullptr");
|
||||||
|
|
||||||
|
struct nf7core_lua_value* dst = &this->args.ptr[i];
|
||||||
|
if (!nf7core_lua_value_set(dst, src)) {
|
||||||
|
nf7core_lua_value_set(dst, &NF7CORE_LUA_VALUE_NIL());
|
||||||
|
nf7util_log_warn(
|
||||||
|
"failed to store parameter value, it's replaced by nil");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 != nf7util_log_uv(uv_timer_start(&this->timer, on_time_, timeout, 0))) {
|
||||||
|
nf7util_log_error(
|
||||||
|
"failed to start timer for resuming thread");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
nf7core_lua_thread_ref(this);
|
||||||
|
|
||||||
|
nf7util_log_debug("lua thread state change: PAUSED -> SCHEDULED");
|
||||||
|
this->state = NF7CORE_LUA_THREAD_SCHEDULED;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* alloc_(void* data, void* ptr, size_t, size_t nsize) {
|
||||||
|
struct nf7core_lua_thread* this = data;
|
||||||
|
return nf7util_malloc_realloc(this->malloc, ptr, nsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void del_(struct nf7core_lua_thread* this) {
|
||||||
|
if (nullptr == this) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (nullptr != this->timer.data) {
|
||||||
|
uv_close((uv_handle_t*) &this->timer, on_close_);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nullptr != this->lua && this->lua_owned) {
|
||||||
|
lua_close(this->lua);
|
||||||
|
nf7util_log_debug("lua state is closed");
|
||||||
|
}
|
||||||
|
if (nullptr != this->base) {
|
||||||
|
nf7core_lua_thread_unref(this->base);
|
||||||
|
}
|
||||||
|
nf7util_malloc_free(this->malloc, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_time_(uv_timer_t* timer) {
|
||||||
|
struct nf7core_lua_thread* this = timer->data;
|
||||||
|
assert(nullptr != this);
|
||||||
|
assert(NF7CORE_LUA_THREAD_SCHEDULED == this->state);
|
||||||
|
|
||||||
|
lua_State* L = this->lua;
|
||||||
|
for (uint32_t i = 0; i < this->args.n; ++i) {
|
||||||
|
struct nf7core_lua_value* v = &this->args.ptr[i];
|
||||||
|
nf7core_lua_value_push(v, L);
|
||||||
|
nf7core_lua_value_unset(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
nf7util_log_debug("lua thread state change: SCHEDULED -> RUNNING");
|
||||||
|
this->state = NF7CORE_LUA_THREAD_RUNNING;
|
||||||
|
|
||||||
|
const int result = lua_resume(L, (int) this->args.n);
|
||||||
|
switch (result) {
|
||||||
|
case 0:
|
||||||
|
nf7util_log_debug("lua thread state change: RUNNING -> DONE");
|
||||||
|
this->state = NF7CORE_LUA_THREAD_DONE;
|
||||||
|
break;
|
||||||
|
case LUA_YIELD:
|
||||||
|
nf7util_log_debug("lua thread state change: RUNNING -> PAUSED");
|
||||||
|
this->state = NF7CORE_LUA_THREAD_PAUSED;
|
||||||
|
break;
|
||||||
|
case LUA_ERRMEM:
|
||||||
|
case LUA_ERRRUN:
|
||||||
|
case LUA_ERRERR:
|
||||||
|
nf7util_log_warn("lua execution failed: ", lua_tostring(L, -1));
|
||||||
|
nf7util_log_debug("lua thread state change: RUNNING -> ABORTED");
|
||||||
|
this->state = NF7CORE_LUA_THREAD_ABORTED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nullptr != this->post_exec) {
|
||||||
|
this->post_exec(this, L);
|
||||||
|
}
|
||||||
|
lua_settop(L, 0);
|
||||||
|
|
||||||
|
nf7core_lua_thread_unref(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_close_(uv_handle_t* handle) {
|
||||||
|
struct nf7core_lua_thread* this = handle->data;
|
||||||
|
this->timer.data = nullptr;
|
||||||
|
del_(this);
|
||||||
|
}
|
92
core/lua/thread.h
Normal file
92
core/lua/thread.h
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// No copyright
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
#include <uv.h>
|
||||||
|
|
||||||
|
#include "util/malloc.h"
|
||||||
|
#include "util/refcnt.h"
|
||||||
|
|
||||||
|
#include "core/lua/mod.h"
|
||||||
|
#include "core/lua/value.h"
|
||||||
|
#include "core/lua/value_ptr.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct nf7core_lua_thread {
|
||||||
|
struct nf7core_lua* mod;
|
||||||
|
struct nf7util_malloc* malloc;
|
||||||
|
uv_loop_t* uv;
|
||||||
|
|
||||||
|
bool lua_owned;
|
||||||
|
lua_State* lua;
|
||||||
|
|
||||||
|
struct nf7core_lua_thread* base;
|
||||||
|
|
||||||
|
uv_timer_t timer;
|
||||||
|
|
||||||
|
uint32_t refcnt;
|
||||||
|
|
||||||
|
uint8_t state;
|
||||||
|
# define NF7CORE_LUA_THREAD_PAUSED 0
|
||||||
|
# define NF7CORE_LUA_THREAD_SCHEDULED 1
|
||||||
|
# define NF7CORE_LUA_THREAD_RUNNING 2
|
||||||
|
# define NF7CORE_LUA_THREAD_DONE 3
|
||||||
|
# define NF7CORE_LUA_THREAD_ABORTED 4
|
||||||
|
|
||||||
|
struct {
|
||||||
|
# define NF7CORE_LUA_THREAD_MAX_ARGS 4
|
||||||
|
uint32_t n;
|
||||||
|
struct nf7core_lua_value ptr[NF7CORE_LUA_THREAD_MAX_ARGS];
|
||||||
|
} args;
|
||||||
|
|
||||||
|
void* data;
|
||||||
|
void (*post_exec)(struct nf7core_lua_thread*, lua_State*);
|
||||||
|
};
|
||||||
|
NF7UTIL_REFCNT_DECL(, nf7core_lua_thread);
|
||||||
|
|
||||||
|
|
||||||
|
// Creates and returns new thread.
|
||||||
|
struct nf7core_lua_thread* nf7core_lua_thread_new(
|
||||||
|
struct nf7core_lua* mod,
|
||||||
|
struct nf7core_lua_thread* base,
|
||||||
|
struct nf7core_lua_value_ptr* func);
|
||||||
|
// POSTCONDS:
|
||||||
|
// - If the base is not nullptr, the returned thread must be synchronized with
|
||||||
|
// the base, otherwise, it's completely independent. (base thread)
|
||||||
|
// - If the func is not nullptr, the returned thread prepares to execute a
|
||||||
|
// function in the value,
|
||||||
|
// otherwise, it can execute nothing (this is for the base thread)
|
||||||
|
|
||||||
|
// Resumes the co-routine with the values.
|
||||||
|
bool nf7core_lua_thread_resume_varg_after(
|
||||||
|
struct nf7core_lua_thread* this, uint64_t timeout, va_list vargs);
|
||||||
|
// PRECONDS:
|
||||||
|
// - `nullptr != this`
|
||||||
|
// - `nullptr != this->func`
|
||||||
|
// - `NF7CORE_LUA_THREAD_PAUSED == this->state`
|
||||||
|
// - Items in `vargs` must be `const struct nf7core_lua_value*` or `nullptr`.
|
||||||
|
// - The last item of `vargs` must be `nullptr`.
|
||||||
|
// POSTCONDS:
|
||||||
|
// - When returns true:
|
||||||
|
// - `NF7CORE_LUA_THREAD_SCHEDULED == this->state`
|
||||||
|
// - the state will changes to RUNNING after `timeout` [ms]
|
||||||
|
// - Otherwise, nothing happens.
|
||||||
|
|
||||||
|
static inline bool nf7core_lua_thread_resume(
|
||||||
|
struct nf7core_lua_thread* this, ...) {
|
||||||
|
va_list vargs;
|
||||||
|
va_start(vargs, this);
|
||||||
|
const bool ret = nf7core_lua_thread_resume_varg_after(this, 0, vargs);
|
||||||
|
va_end(vargs);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
static inline bool nf7core_lua_thread_resume_after(
|
||||||
|
struct nf7core_lua_thread* this, uint64_t timeout, ...) {
|
||||||
|
va_list vargs;
|
||||||
|
va_start(vargs, this);
|
||||||
|
const bool ret = nf7core_lua_thread_resume_varg_after(this, timeout, vargs);
|
||||||
|
va_end(vargs);
|
||||||
|
return ret;
|
||||||
|
}
|
67
core/lua/thread.test.c
Normal file
67
core/lua/thread.test.c
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// No copyright
|
||||||
|
#include "core/lua/thread.h"
|
||||||
|
|
||||||
|
#include <lauxlib.h>
|
||||||
|
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
|
#include "test/common.h"
|
||||||
|
|
||||||
|
|
||||||
|
static void finalize_(struct nf7core_lua_thread* this, lua_State*) {
|
||||||
|
struct nf7test* test_ = this->data;
|
||||||
|
nf7test_expect(NF7CORE_LUA_THREAD_DONE == this->state);
|
||||||
|
nf7test_unref(test_);
|
||||||
|
}
|
||||||
|
|
||||||
|
NF7TEST(nf7core_lua_thread_test_valid_syntax) {
|
||||||
|
struct nf7core_lua* mod =
|
||||||
|
(void*) nf7_get_mod_by_meta(test_->nf7, &nf7core_lua);
|
||||||
|
if (!nf7test_expect(nullptr != mod)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct nf7core_lua_thread* base = mod->thread;
|
||||||
|
lua_State* L = base->lua;
|
||||||
|
|
||||||
|
if (!nf7test_expect(0 == luaL_loadstring(L, "local x = 100"))) {
|
||||||
|
nf7util_log_error("lua compile error: %s", lua_tostring(L, -1));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct nf7core_lua_value_ptr* func = nf7core_lua_value_ptr_new(base, L);
|
||||||
|
if (!nf7test_expect(nullptr != func)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct nf7core_lua_thread* thread = nf7core_lua_thread_new(mod, base, func);
|
||||||
|
nf7core_lua_value_ptr_unref(func);
|
||||||
|
if (!nf7test_expect(nullptr != thread)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
thread->data = test_;
|
||||||
|
thread->post_exec = finalize_;
|
||||||
|
|
||||||
|
if (!nf7test_expect(nf7core_lua_thread_resume(thread, nullptr))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
nf7core_lua_thread_unref(thread);
|
||||||
|
nf7test_ref(test_);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
NF7TEST(nf7core_lua_thread_test_invalid_syntax) {
|
||||||
|
struct nf7core_lua* mod =
|
||||||
|
(void*) nf7_get_mod_by_meta(test_->nf7, &nf7core_lua);
|
||||||
|
if (!nf7test_expect(nullptr != mod)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct nf7core_lua_thread* base = mod->thread;
|
||||||
|
lua_State* L = base->lua;
|
||||||
|
|
||||||
|
if (!nf7test_expect(0 != luaL_loadstring(L, "helloworld"))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
88
core/lua/value.h
Normal file
88
core/lua/value.h
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
// No copyright
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "core/lua/value_ptr.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct nf7core_lua_value {
|
||||||
|
uint32_t type;
|
||||||
|
# define NF7CORE_LUA_VALUE_TYPE_NIL 0
|
||||||
|
# define NF7CORE_LUA_VALUE_TYPE_INT 1
|
||||||
|
# define NF7CORE_LUA_VALUE_TYPE_NUM 2
|
||||||
|
# define NF7CORE_LUA_VALUE_TYPE_PTR 3
|
||||||
|
union {
|
||||||
|
lua_Integer i;
|
||||||
|
lua_Number n;
|
||||||
|
struct nf7core_lua_value_ptr* ptr;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NF7CORE_LUA_VALUE_NIL() \
|
||||||
|
(struct nf7core_lua_value) { .type = NF7CORE_LUA_VALUE_TYPE_NIL, }
|
||||||
|
#define NF7CORE_LUA_VALUE_INT(v) \
|
||||||
|
(struct nf7core_lua_value) { .type = NF7CORE_LUA_VALUE_TYPE_INT, .i = (v), }
|
||||||
|
#define NF7CORE_LUA_VALUE_NUM(v) \
|
||||||
|
(struct nf7core_lua_value) { .type = NF7CORE_LUA_VALUE_TYPE_NUM, .n = (v), }
|
||||||
|
#define NF7CORE_LUA_VALUE_PTR(v) \
|
||||||
|
(struct nf7core_lua_value) { \
|
||||||
|
.type = NF7CORE_LUA_VALUE_TYPE_PTR, \
|
||||||
|
.ptr = (v), \
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void nf7core_lua_value_push(
|
||||||
|
const struct nf7core_lua_value* this, lua_State* L) {
|
||||||
|
assert(nullptr != this);
|
||||||
|
assert(nullptr != L);
|
||||||
|
|
||||||
|
switch (this->type) {
|
||||||
|
case NF7CORE_LUA_VALUE_TYPE_NIL:
|
||||||
|
lua_pushnil(L);
|
||||||
|
break;
|
||||||
|
case NF7CORE_LUA_VALUE_TYPE_INT:
|
||||||
|
lua_pushinteger(L, this->i);
|
||||||
|
break;
|
||||||
|
case NF7CORE_LUA_VALUE_TYPE_NUM:
|
||||||
|
lua_pushnumber(L, this->n);
|
||||||
|
break;
|
||||||
|
case NF7CORE_LUA_VALUE_TYPE_PTR:
|
||||||
|
assert(nullptr != this->ptr);
|
||||||
|
nf7core_lua_value_ptr_push(this->ptr, L);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void nf7core_lua_value_unset(struct nf7core_lua_value* this) {
|
||||||
|
assert(nullptr != this);
|
||||||
|
|
||||||
|
switch (this->type) {
|
||||||
|
case NF7CORE_LUA_VALUE_TYPE_PTR:
|
||||||
|
nf7core_lua_value_ptr_unref(this->ptr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this->type = NF7CORE_LUA_VALUE_TYPE_NIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool nf7core_lua_value_set(
|
||||||
|
struct nf7core_lua_value* this, const struct nf7core_lua_value* src) {
|
||||||
|
assert(nullptr != this);
|
||||||
|
assert(nullptr != src);
|
||||||
|
|
||||||
|
nf7core_lua_value_unset(this);
|
||||||
|
|
||||||
|
switch (src->type) {
|
||||||
|
case NF7CORE_LUA_VALUE_TYPE_PTR:
|
||||||
|
*this = *src;
|
||||||
|
nf7core_lua_value_ptr_ref(this->ptr);
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
*this = *src;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
64
core/lua/value_ptr.c
Normal file
64
core/lua/value_ptr.c
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// No copyright
|
||||||
|
#include "core/lua/value_ptr.h"
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
#include <lauxlib.h>
|
||||||
|
|
||||||
|
#include "core/lua/thread.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct nf7core_lua_value_ptr {
|
||||||
|
struct nf7core_lua_thread* thread;
|
||||||
|
struct nf7util_malloc* malloc;
|
||||||
|
lua_State* lua;
|
||||||
|
|
||||||
|
uint32_t refcnt;
|
||||||
|
int index;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void del_(struct nf7core_lua_value_ptr*);
|
||||||
|
|
||||||
|
NF7UTIL_REFCNT_IMPL(, nf7core_lua_value_ptr, {del_(this);});
|
||||||
|
|
||||||
|
|
||||||
|
struct nf7core_lua_value_ptr* nf7core_lua_value_ptr_new(
|
||||||
|
struct nf7core_lua_thread* thread, lua_State* L) {
|
||||||
|
assert(nullptr != thread);
|
||||||
|
assert(nullptr != L);
|
||||||
|
|
||||||
|
struct nf7core_lua_value_ptr* this =
|
||||||
|
nf7util_malloc_alloc(thread->malloc, sizeof(*this));
|
||||||
|
if (nullptr == this) {
|
||||||
|
goto ABORT;
|
||||||
|
}
|
||||||
|
*this = (struct nf7core_lua_value_ptr) {
|
||||||
|
.malloc = thread->malloc,
|
||||||
|
.lua = L,
|
||||||
|
};
|
||||||
|
|
||||||
|
this->thread = thread;
|
||||||
|
nf7core_lua_thread_ref(this->thread);
|
||||||
|
|
||||||
|
this->index = luaL_ref(this->lua, LUA_REGISTRYINDEX);
|
||||||
|
|
||||||
|
nf7core_lua_value_ptr_ref(this);
|
||||||
|
return this;
|
||||||
|
|
||||||
|
ABORT:
|
||||||
|
lua_pop(L, 1);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nf7core_lua_value_ptr_push(
|
||||||
|
const struct nf7core_lua_value_ptr* this, lua_State* lua) {
|
||||||
|
assert(nullptr != this);
|
||||||
|
assert(nullptr != lua);
|
||||||
|
lua_rawgeti(lua, LUA_REGISTRYINDEX, this->index);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void del_(struct nf7core_lua_value_ptr* this) {
|
||||||
|
assert(nullptr != this);
|
||||||
|
luaL_unref(this->lua, LUA_REGISTRYINDEX, this->index);
|
||||||
|
nf7core_lua_thread_unref(this->thread);
|
||||||
|
nf7util_malloc_free(this->malloc, this);
|
||||||
|
}
|
22
core/lua/value_ptr.h
Normal file
22
core/lua/value_ptr.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// No copyright
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
|
||||||
|
#include "util/refcnt.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct nf7core_lua_thread;
|
||||||
|
|
||||||
|
struct nf7core_lua_value_ptr;
|
||||||
|
NF7UTIL_REFCNT_DECL(, nf7core_lua_value_ptr);
|
||||||
|
|
||||||
|
struct nf7core_lua_value_ptr* nf7core_lua_value_ptr_new(
|
||||||
|
struct nf7core_lua_thread*, lua_State*);
|
||||||
|
// POSTCONDS:
|
||||||
|
// - the top value is always popped
|
||||||
|
|
||||||
|
void nf7core_lua_value_ptr_push(
|
||||||
|
const struct nf7core_lua_value_ptr*, lua_State*);
|
14
core/null/CMakeLists.txt
Normal file
14
core/null/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
add_library(nf7core_null)
|
||||||
|
target_sources(nf7core_null
|
||||||
|
PRIVATE
|
||||||
|
idea.c
|
||||||
|
mod.c
|
||||||
|
PUBLIC
|
||||||
|
mod.h
|
||||||
|
)
|
||||||
|
target_link_libraries(nf7core_null
|
||||||
|
PRIVATE
|
||||||
|
nf7if
|
||||||
|
nf7util
|
||||||
|
)
|
||||||
|
|
64
core/null/idea.c
Normal file
64
core/null/idea.c
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// No copyright
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "nf7.h"
|
||||||
|
|
||||||
|
#include "util/malloc.h"
|
||||||
|
|
||||||
|
#include "core/exec/mod.h"
|
||||||
|
#include "core/null/mod.h"
|
||||||
|
|
||||||
|
|
||||||
|
static struct nf7core_exec_entity* new_(struct nf7core_exec*);
|
||||||
|
|
||||||
|
struct nf7core_exec_entity* nf7core_null_entity_new(struct nf7* nf7) {
|
||||||
|
assert(nullptr != nf7);
|
||||||
|
|
||||||
|
struct nf7core_exec* exec = (void*) nf7_get_mod_by_meta(nf7, &nf7core_exec);
|
||||||
|
if (nullptr == exec) {
|
||||||
|
nf7util_log_error("nf7core_exec module is missing");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return new_(exec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct nf7core_exec_entity* new_(struct nf7core_exec* mod) {
|
||||||
|
assert(nullptr != mod);
|
||||||
|
|
||||||
|
const struct nf7* nf7 = mod->super.nf7;
|
||||||
|
|
||||||
|
struct nf7core_exec_entity* this =
|
||||||
|
nf7util_malloc_alloc(nf7->malloc, sizeof(*this));
|
||||||
|
if (nullptr == this) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
*this = (struct nf7core_exec_entity) {
|
||||||
|
.idea = &nf7core_null_idea,
|
||||||
|
.mod = mod,
|
||||||
|
};
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void del_(struct nf7core_exec_entity* this) {
|
||||||
|
if (nullptr != this) {
|
||||||
|
assert(nullptr != this->mod);
|
||||||
|
|
||||||
|
const struct nf7* nf7 = this->mod->super.nf7;
|
||||||
|
assert(nullptr != nf7);
|
||||||
|
assert(nullptr != nf7->malloc);
|
||||||
|
|
||||||
|
nf7util_malloc_free(nf7->malloc, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_(struct nf7core_exec_entity*, struct nf7util_buffer*) { }
|
||||||
|
|
||||||
|
const struct nf7core_exec_idea nf7core_null_idea = {
|
||||||
|
.name = (const uint8_t*) "nf7core_null_idea",
|
||||||
|
.details = (const uint8_t*) "null implementation of an idea",
|
||||||
|
|
||||||
|
.new = new_,
|
||||||
|
.del = del_,
|
||||||
|
.send = send_,
|
||||||
|
};
|
41
core/null/mod.c
Normal file
41
core/null/mod.c
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// No copyright
|
||||||
|
#include "core/null/mod.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "util/log.h"
|
||||||
|
#include "util/malloc.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct nf7_mod* nf7core_null_new(struct nf7* nf7) {
|
||||||
|
assert(nullptr != nf7);
|
||||||
|
|
||||||
|
struct nf7_mod* this = nf7util_malloc_alloc(nf7->malloc, sizeof(*this));
|
||||||
|
if (nullptr == this) {
|
||||||
|
nf7util_log_error("failed to allocate module context");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
*this = (struct nf7_mod) {
|
||||||
|
.nf7 = nf7,
|
||||||
|
.meta = &nf7core_null,
|
||||||
|
};
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void del_(struct nf7_mod* this) {
|
||||||
|
if (nullptr != this) {
|
||||||
|
assert(nullptr != this->nf7);
|
||||||
|
assert(nullptr != this->nf7->malloc);
|
||||||
|
|
||||||
|
nf7util_malloc_free(this->nf7->malloc, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct nf7_mod_meta nf7core_null = {
|
||||||
|
.name = (const uint8_t*) "nf7core_null",
|
||||||
|
.desc = (const uint8_t*) "null implementations of each interfaces",
|
||||||
|
.ver = NF7_VERSION,
|
||||||
|
|
||||||
|
.del = del_,
|
||||||
|
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user