Compare commits
2 Commits
c6dfc4b43c
...
9a96bb6524
Author | SHA1 | Date | |
---|---|---|---|
9a96bb6524 | |||
98d6a7a101 |
@ -105,8 +105,6 @@ target_sources(nf7
|
||||
common/gui_dnd.hh
|
||||
common/gui_timeline.hh
|
||||
common/gui_timeline.cc
|
||||
common/gui_value.hh
|
||||
common/gui_value.cc
|
||||
common/gui_window.hh
|
||||
common/gui_window.cc
|
||||
common/history.hh
|
||||
@ -120,6 +118,7 @@ target_sources(nf7
|
||||
common/luajit_ref.hh
|
||||
common/luajit_thread.hh
|
||||
common/luajit_thread.cc
|
||||
common/luajit_value.cc
|
||||
common/memento.hh
|
||||
common/memento_recorder.hh
|
||||
common/mutex.hh
|
||||
@ -132,7 +131,6 @@ target_sources(nf7
|
||||
common/pure_node_file.hh
|
||||
common/queue.hh
|
||||
common/ring_buffer.hh
|
||||
common/sequencer.hh
|
||||
common/squashed_history.hh
|
||||
common/stopwatch.hh
|
||||
common/task.hh
|
||||
@ -164,9 +162,6 @@ target_sources(nf7
|
||||
file/node_ref.cc
|
||||
file/node_singleton.cc
|
||||
file/node_ziptie.cc
|
||||
file/sequencer_adaptor.cc
|
||||
file/sequencer_call.cc
|
||||
file/sequencer_timeline.cc
|
||||
file/system_dir.cc
|
||||
file/system_event.cc
|
||||
file/system_imgui.cc
|
||||
|
@ -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
|
594
common/luajit.cc
594
common/luajit.cc
@ -14,410 +14,173 @@
|
||||
#include "common/logger.hh"
|
||||
#include "common/logger_ref.hh"
|
||||
#include "common/luajit_thread.hh"
|
||||
#include "common/luajit_std.hh"
|
||||
|
||||
|
||||
namespace nf7::luajit {
|
||||
|
||||
// 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 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;
|
||||
auto operator()(const Value::Pulse&) noexcept { lua_pushnil(L); }
|
||||
auto operator()(const Value::Boolean& v) noexcept { lua_pushboolean(L, v); }
|
||||
auto operator()(const Value::Integer& v) noexcept { lua_pushinteger(L, v); }
|
||||
auto operator()(const Value::Scalar& v) noexcept { lua_pushnumber(L, v); }
|
||||
auto operator()(const Value::String& v) noexcept { lua_pushstring(L, v.c_str()); }
|
||||
auto operator()(const Value::ConstVector& v) noexcept { PushVector(L, v); }
|
||||
auto operator()(const Value::DataPtr&) noexcept { lua_pushnil(L); }
|
||||
|
||||
auto operator()(const Value::ConstTuple& v) noexcept {
|
||||
const auto& tup = *v;
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
std::visit(Visitor {.L = L}, v.value());
|
||||
return 1;
|
||||
});
|
||||
lua_setfield(L, -2, "value");
|
||||
lua_setfield(L, -2, "__index");
|
||||
template <> void PushMeta<StdTable>(lua_State* L) noexcept {
|
||||
if (!luaL_newmetatable(L, MetaName<StdTable>::kValue)) {
|
||||
return;
|
||||
}
|
||||
luaL_openlibs(L);
|
||||
lua_createtable(L, 0, 0);
|
||||
{
|
||||
// ---- time lib ----
|
||||
|
||||
// now()
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
CheckRef<nf7::Value>(L, 1, "nf7::Value").~Value();
|
||||
return 0;
|
||||
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, "__gc");
|
||||
}
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
std::optional<nf7::Value> ToValue(lua_State* L, int idx) noexcept {
|
||||
// get absolute position on stack because recursion call may occur
|
||||
if (idx < 0) {
|
||||
idx = lua_gettop(L)+idx+1;
|
||||
}
|
||||
lua_setfield(L, -2, "now");
|
||||
|
||||
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_type(L, -2) == LUA_TSTRING) {
|
||||
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;
|
||||
}
|
||||
|
||||
void PushVector(lua_State* L, const nf7::Value::ConstVector& v) noexcept {
|
||||
static const char* kTypeName = "nf7::Value::ConstVector";
|
||||
using T = nf7::Value::ConstVector;
|
||||
|
||||
assert(v != nullptr);
|
||||
new (lua_newuserdata(L, sizeof(v))) nf7::Value::ConstVector(v);
|
||||
|
||||
if (luaL_newmetatable(L, kTypeName)) {
|
||||
lua_createtable(L, 0, 0);
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
const auto& v = CheckRef<T>(L, 1, kTypeName);
|
||||
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<T>(L, 1, kTypeName);
|
||||
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<T>(L, 1, kTypeName);
|
||||
lua_pushinteger(L, static_cast<lua_Integer>(v->size()));
|
||||
return 1;
|
||||
});
|
||||
lua_setfield(L, -2, "size");
|
||||
lua_setfield(L, -2, "__index");
|
||||
// ---- value lib ----
|
||||
|
||||
// value(entity) -> value
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
CheckRef<T>(L, 1, kTypeName).~shared_ptr();
|
||||
return 0;
|
||||
});
|
||||
lua_setfield(L, -2, "__gc");
|
||||
}
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
|
||||
void PushMutableVector(lua_State* L, std::vector<uint8_t>&& v) noexcept {
|
||||
constexpr const char* kTypeName = "nf7::Value::MutableVector";
|
||||
using T = std::vector<uint8_t>;
|
||||
|
||||
new (lua_newuserdata(L, sizeof(v))) T(std::move(v));
|
||||
if (luaL_newmetatable(L, kTypeName)) {
|
||||
lua_createtable(L, 0, 0);
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
auto& v = CheckRef<T>(L, 1, kTypeName);
|
||||
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<T>(L, 1, kTypeName);
|
||||
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<T>(L, 1, kTypeName);
|
||||
const auto dst_off = luaL_checkinteger(L, 2);
|
||||
|
||||
const T* src;
|
||||
if (const auto& v = ToVector(L, 3)) {
|
||||
src = &**v;
|
||||
} else if (const auto& mv = ToMutableVector(L, 3)) {
|
||||
src = &*mv;
|
||||
if (lua_isstring(L, 2)) {
|
||||
const auto type = std::string_view {lua_tostring(L, 2)};
|
||||
if (type == "integer" || type == "int") {
|
||||
Push(L, static_cast<nf7::Value::Integer>(luaL_checkinteger(L, 1)));
|
||||
} else {
|
||||
return luaL_error(L, "#2 argument must be vector or mutable vector");
|
||||
return luaL_error(L, "unknown type specifier: %s", type);
|
||||
}
|
||||
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<T>(L, 1, kTypeName).~vector();
|
||||
return 0;
|
||||
} else {
|
||||
Push(L, Check<nf7::Value>(L, 1));
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
lua_setfield(L, -2, "__gc");
|
||||
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");
|
||||
|
||||
|
||||
// ---- lua std libs ----
|
||||
const auto Copy =
|
||||
[L](const char* name, const char* expr, bool imm) {
|
||||
luaL_loadstring(L, expr);
|
||||
lua_call(L, 0, 1);
|
||||
if (imm) {
|
||||
Push(L, ImmTable {});
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
lua_setfield(L, -2, name);
|
||||
};
|
||||
Copy("assert", "return assert", false);
|
||||
Copy("error", "return error", false);
|
||||
Copy("ipairs", "return ipairs", false);
|
||||
Copy("loadstring", "return loadstring", false);
|
||||
Copy("next", "return next", false);
|
||||
Copy("pairs", "return pairs", false);
|
||||
Copy("pcall", "return pcall", false);
|
||||
Copy("rawequal", "return rawequal", false);
|
||||
Copy("rawget", "return rawget", false);
|
||||
Copy("select", "return select", false);
|
||||
Copy("setfenv", "return setfenv", false);
|
||||
Copy("setmetatable", "return setmetatable", false);
|
||||
Copy("tonumber", "return tonumber", false);
|
||||
Copy("tostring", "return tostring", false);
|
||||
Copy("type", "return type", false);
|
||||
Copy("unpack", "return unpack", false);
|
||||
Copy("_VERSION", "return _VERSION", false);
|
||||
Copy("xpcall", "return xpcall", false);
|
||||
|
||||
Copy("bit", "return require(\"bit\")", true);
|
||||
Copy("coroutine", "return coroutine", true);
|
||||
Copy("math", "return math", true);
|
||||
Copy("string", "return string", true);
|
||||
Copy("table", "return table", true);
|
||||
}
|
||||
lua_setmetatable(L, -2);
|
||||
lua_setfield(L, -2, "__index");
|
||||
}
|
||||
|
||||
void PushNodeRootLambda(
|
||||
lua_State* L, const std::shared_ptr<nf7::NodeRootLambda>& la) noexcept {
|
||||
assert(la);
|
||||
|
||||
template <> void PushMeta<std::shared_ptr<nf7::NodeRootLambda>>(lua_State* L) noexcept {
|
||||
using T = std::shared_ptr<nf7::NodeRootLambda>;
|
||||
new (lua_newuserdata(L, sizeof(T))) T {la};
|
||||
if (!luaL_newmetatable(L, MetaName<T>::kValue)) {
|
||||
return;
|
||||
}
|
||||
lua_createtable(L, 0, 0);
|
||||
{
|
||||
// la:send(nf7, key, value)
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
auto la = Check<T>(L, 1);
|
||||
la->ExecSend(luaL_checkstring(L, 2), Check<nf7::Value>(L, 3));
|
||||
return 0;
|
||||
});
|
||||
lua_setfield(L, -2, "send");
|
||||
|
||||
if (luaL_newmetatable(L, "nf7::NodeRootLambda")) {
|
||||
lua_createtable(L, 0, 0);
|
||||
{
|
||||
// la:send(nf7, key, value)
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
auto la = CheckNodeRootLambda(L, 1);
|
||||
la->ExecSend(luaL_checkstring(L, 2), luajit::CheckValue(L, 3));
|
||||
// la:recv(nf7, {name1, name2, ...})
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
auto la = Check<T>(L, 1);
|
||||
auto th = luajit::Thread::GetPtr(L, 2);
|
||||
|
||||
const auto names = Check<std::vector<std::string>>(L, 3);
|
||||
if (names.size() == 0) {
|
||||
return 0;
|
||||
});
|
||||
lua_setfield(L, -2, "send");
|
||||
}
|
||||
|
||||
// la:recv(nf7, {name1, name2, ...})
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
auto la = CheckNodeRootLambda(L, 1);
|
||||
auto th = luajit::Thread::GetPtr(L, 2);
|
||||
|
||||
std::vector<std::string> names;
|
||||
ToStringList(L, 3, names);
|
||||
if (names.size() == 0) {
|
||||
auto fu = la->Select(
|
||||
std::unordered_set<std::string>(names.begin(), names.end()));
|
||||
if (fu.done()) {
|
||||
try {
|
||||
const auto& p = fu.value();
|
||||
lua_pushstring(L, p.first.c_str());
|
||||
luajit::Push(L, p.second);
|
||||
return 2;
|
||||
} catch (nf7::Exception&) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto fu = la->Select(
|
||||
std::unordered_set<std::string>(names.begin(), names.end()));
|
||||
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&) {
|
||||
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);
|
||||
});
|
||||
return th->Yield(L, la);
|
||||
}
|
||||
});
|
||||
lua_setfield(L, -2, "recv");
|
||||
}
|
||||
lua_setfield(L, -2, "__index");
|
||||
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
CheckNodeRootLambda(L, 1).~shared_ptr();
|
||||
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);
|
||||
});
|
||||
return th->Yield(L, la);
|
||||
}
|
||||
});
|
||||
lua_setfield(L, -2, "__gc");
|
||||
lua_setfield(L, -2, "recv");
|
||||
}
|
||||
lua_setmetatable(L, -2);
|
||||
lua_setfield(L, -2, "__index");
|
||||
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
Check<T>(L, 1).~shared_ptr();
|
||||
return 0;
|
||||
});
|
||||
lua_setfield(L, -2, "__gc");
|
||||
}
|
||||
|
||||
|
||||
void PushGlobalTable(lua_State* L) noexcept {
|
||||
void Push(lua_State* L, GlobalTable) noexcept {
|
||||
if (luaL_newmetatable(L, "nf7::luajit::GlobalTable")) {
|
||||
PushStdTable(L);
|
||||
Push(L, StdTable {});
|
||||
lua_setfield(L, -2, "std");
|
||||
}
|
||||
}
|
||||
void PushImmEnv(lua_State* L) noexcept {
|
||||
void Push(lua_State* L, ImmEnv) noexcept {
|
||||
if (luaL_newmetatable(L, "nf7::luajit::ImmEnv")) {
|
||||
lua_createtable(L, 0, 0);
|
||||
{
|
||||
PushGlobalTable(L);
|
||||
Push(L, GlobalTable {});
|
||||
lua_setfield(L, -2, "__index");
|
||||
|
||||
lua_pushcfunction(L, [](auto L) { return luaL_error(L, "global is immutable"); });
|
||||
@ -426,100 +189,11 @@ void PushImmEnv(lua_State* L) noexcept {
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
}
|
||||
void PushImmTable(lua_State* L) noexcept {
|
||||
void Push(lua_State* L, ImmTable) noexcept {
|
||||
if (luaL_newmetatable(L, "nf7::luajit::ImmTable")) {
|
||||
lua_pushcfunction(L, [](auto L) { return luaL_error(L, "table is immutable"); });
|
||||
lua_setfield(L, -2, "__newindex");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
209
common/luajit.hh
209
common/luajit.hh
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
@ -15,6 +17,14 @@
|
||||
|
||||
namespace nf7::luajit {
|
||||
|
||||
// special types for using with Push<T> functions
|
||||
struct Nil { };
|
||||
struct GlobalTable { };
|
||||
struct StdTable { };
|
||||
struct ImmEnv { };
|
||||
struct ImmTable { };
|
||||
|
||||
|
||||
// ---- utility
|
||||
inline bool MatchMetaName(lua_State* L, int idx, const char* type) noexcept {
|
||||
if (0 == lua_getmetatable(L, idx)) {
|
||||
@ -26,103 +36,76 @@ inline bool MatchMetaName(lua_State* L, int idx, const char* type) noexcept {
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// ---- udata utility
|
||||
template <typename T, typename... Args>
|
||||
inline T& NewUserData(lua_State* L, Args&&... args) noexcept {
|
||||
inline T& NewUData(lua_State* L, Args&&... args) noexcept {
|
||||
return *(new (lua_newuserdata(L, sizeof(T))) T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
|
||||
// ---- reference conversion
|
||||
template <typename T>
|
||||
inline T* ToRef(lua_State* L, int idx, const char* type) noexcept {
|
||||
inline T* PeekUData(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 T& CheckRef(lua_State* L, int idx, const char* type) {
|
||||
inline T& CheckUData(lua_State* L, int idx, const char* type) {
|
||||
return *reinterpret_cast<T*>(luaL_checkudata(L, idx, type));
|
||||
}
|
||||
|
||||
|
||||
// ---- Value conversion
|
||||
void PushValue(lua_State*, const nf7::Value&) noexcept;
|
||||
std::optional<nf7::Value> ToValue(lua_State*, int) noexcept;
|
||||
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);
|
||||
}
|
||||
// ---- PushMeta functions
|
||||
template <typename T> void PushMeta(lua_State*) noexcept;
|
||||
|
||||
void PushVector(lua_State*, const nf7::Value::ConstVector&) noexcept;
|
||||
inline 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;
|
||||
}
|
||||
template <> void PushMeta<StdTable>(lua_State*) noexcept;
|
||||
|
||||
void PushMutableVector(lua_State*, std::vector<uint8_t>&&) noexcept;
|
||||
inline 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);
|
||||
}
|
||||
template <> void PushMeta<nf7::Value>(lua_State*) noexcept;
|
||||
template <> void PushMeta<nf7::Value::Buffer>(lua_State*) noexcept;
|
||||
template <> void PushMeta<nf7::Value::Tuple>(lua_State*) noexcept;
|
||||
|
||||
void PushNodeRootLambda(
|
||||
lua_State*, const std::shared_ptr<nf7::NodeRootLambda>&) noexcept;
|
||||
inline const std::shared_ptr<nf7::NodeRootLambda>& CheckNodeRootLambda(lua_State* L, int idx) {
|
||||
return CheckRef<std::shared_ptr<nf7::NodeRootLambda>>(L, idx, "nf7::NodeRootLambda");
|
||||
}
|
||||
|
||||
inline void ToStringList(lua_State* L, int idx, std::vector<std::string>& v) noexcept {
|
||||
v.clear();
|
||||
if (!lua_istable(L, idx)) {
|
||||
if (auto str = lua_tostring(L, idx)) {
|
||||
v.emplace_back(str);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t n = lua_objlen(L, idx);
|
||||
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);
|
||||
}
|
||||
}
|
||||
template <> void PushMeta<std::shared_ptr<nf7::NodeRootLambda>>(lua_State*) noexcept;
|
||||
|
||||
|
||||
// ---- overloaded Push function for template
|
||||
template <typename T>
|
||||
template <typename T> struct MetaName;
|
||||
|
||||
#define DEF_(T) template <> struct MetaName<T> { static constexpr auto kValue = #T; };
|
||||
DEF_(StdTable);
|
||||
DEF_(nf7::Value);
|
||||
DEF_(nf7::Value::Buffer);
|
||||
DEF_(nf7::Value::Tuple);
|
||||
DEF_(std::shared_ptr<nf7::NodeRootLambda>);
|
||||
#undef DEF_
|
||||
|
||||
template <typename T> concept HasMeta = requires (lua_State* L, T& t) {
|
||||
MetaName<T>::kValue;
|
||||
PushMeta<T>(L);
|
||||
};
|
||||
static_assert(HasMeta<nf7::Value>);
|
||||
|
||||
|
||||
// ---- Push functions
|
||||
template <std::integral 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"); }();
|
||||
}
|
||||
lua_pushinteger(L, static_cast<lua_Integer>(v));
|
||||
}
|
||||
template <std::floating_point T>
|
||||
void Push(lua_State* L, T v) noexcept {
|
||||
lua_pushnumber(L, static_cast<lua_Number>(v));
|
||||
}
|
||||
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::NodeRootLambda>& la) noexcept {
|
||||
luajit::PushNodeRootLambda(L, la);
|
||||
inline void Push(lua_State* L, Nil) noexcept { lua_pushnil(L); }
|
||||
inline void Push(lua_State* L, nf7::Value::Pulse) noexcept { lua_pushnil(L); }
|
||||
|
||||
void Push(lua_State*, GlobalTable) noexcept;
|
||||
void Push(lua_State*, StdTable) noexcept;
|
||||
void Push(lua_State*, ImmEnv) noexcept;
|
||||
void Push(lua_State*, ImmTable) noexcept;
|
||||
|
||||
template <HasMeta T>
|
||||
void Push(lua_State* L, const T& v) noexcept {
|
||||
NewUData<T>(L, v);
|
||||
PushMeta<T>(L);
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
|
||||
// pushes all args and returns a number of them
|
||||
@ -140,9 +123,75 @@ int PushAll(lua_State* L, T v, Args&&... args) noexcept {
|
||||
}
|
||||
|
||||
|
||||
// ---- global table
|
||||
void PushGlobalTable(lua_State*) noexcept;
|
||||
void PushImmEnv(lua_State*) noexcept;
|
||||
void PushImmTable(lua_State*) noexcept;
|
||||
// ---- Peek functions
|
||||
template <std::integral T>
|
||||
bool Peek(lua_State* L, int i, T& v) noexcept {
|
||||
if (!lua_isnumber(L, i)) return false;
|
||||
v = static_cast<T>(lua_tointeger(L, i));
|
||||
return true;
|
||||
}
|
||||
template <std::floating_point T>
|
||||
bool Peek(lua_State* L, int i, T& v) noexcept {
|
||||
if (!lua_isnumber(L, i)) return false;
|
||||
v = static_cast<T>(lua_tonumber(L, i));
|
||||
return true;
|
||||
}
|
||||
inline bool Peek(lua_State* L, int i, std::string& v) noexcept {
|
||||
if (!lua_isstring(L, i)) return false;
|
||||
v = lua_tostring(L, i);;
|
||||
return true;
|
||||
}
|
||||
inline bool Peek(lua_State* L, int i, std::string_view& v) noexcept {
|
||||
if (!lua_isstring(L, i)) return false;
|
||||
v = lua_tostring(L, i);;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool Peek(lua_State* L, int idx, std::vector<std::string>& v) noexcept {
|
||||
v.clear();
|
||||
if (!lua_istable(L, idx)) {
|
||||
if (auto str = lua_tostring(L, idx)) {
|
||||
v.emplace_back(str);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const size_t n = lua_objlen(L, idx);
|
||||
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);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <HasMeta T>
|
||||
inline bool Peek(lua_State* L, int idx, T& v) noexcept {
|
||||
if (MatchMetaName(L, idx, MetaName<T>::kValue)) {
|
||||
v = *reinterpret_cast<T*>(lua_touserdata(L, idx));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
std::optional<T> Peek(lua_State* L, int i) noexcept {
|
||||
T v;
|
||||
return Peek(L, i, v)? std::optional<T> {v}: std::nullopt;
|
||||
}
|
||||
inline void Check(lua_State* L, int i, auto& v) {
|
||||
if (!Peek(L, i, v)) {
|
||||
luaL_error(L, "incompatible cast");
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
T Check(lua_State* L, int i) {
|
||||
T v;
|
||||
Check(L, i, v);
|
||||
return v;
|
||||
}
|
||||
|
||||
} // namespace nf7
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
namespace nf7::luajit {
|
||||
|
||||
class Ref final : public nf7::Value::Data {
|
||||
class Ref final {
|
||||
public:
|
||||
Ref() = delete;
|
||||
Ref(const std::shared_ptr<nf7::Context>& ctx,
|
||||
|
@ -1,105 +0,0 @@
|
||||
#include <lua.hpp>
|
||||
|
||||
#include "common/luajit.hh"
|
||||
|
||||
|
||||
namespace nf7::luajit {
|
||||
|
||||
inline void PushStdTable(lua_State* L) noexcept {
|
||||
luaL_openlibs(L);
|
||||
|
||||
lua_newuserdata(L, 0);
|
||||
lua_createtable(L, 0, 0);
|
||||
lua_createtable(L, 0, 0);
|
||||
{
|
||||
// ---- 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");
|
||||
|
||||
|
||||
// ---- lua std libs ----
|
||||
const auto Copy =
|
||||
[L](const char* name, const char* expr, bool imm) {
|
||||
luaL_loadstring(L, expr);
|
||||
lua_call(L, 0, 1);
|
||||
if (imm) {
|
||||
PushImmTable(L);
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
lua_setfield(L, -2, name);
|
||||
};
|
||||
Copy("assert", "return assert", false);
|
||||
Copy("error", "return error", false);
|
||||
Copy("ipairs", "return ipairs", false);
|
||||
Copy("loadstring", "return loadstring", false);
|
||||
Copy("next", "return next", false);
|
||||
Copy("pairs", "return pairs", false);
|
||||
Copy("pcall", "return pcall", false);
|
||||
Copy("rawequal", "return rawequal", false);
|
||||
Copy("rawget", "return rawget", false);
|
||||
Copy("select", "return select", false);
|
||||
Copy("setfenv", "return setfenv", false);
|
||||
Copy("setmetatable", "return setmetatable", false);
|
||||
Copy("tonumber", "return tonumber", false);
|
||||
Copy("tostring", "return tostring", false);
|
||||
Copy("type", "return type", false);
|
||||
Copy("unpack", "return unpack", false);
|
||||
Copy("_VERSION", "return _VERSION", false);
|
||||
Copy("xpcall", "return xpcall", false);
|
||||
|
||||
Copy("bit", "return require(\"bit\")", true);
|
||||
Copy("coroutine", "return coroutine", true);
|
||||
Copy("math", "return math", true);
|
||||
Copy("string", "return string", true);
|
||||
Copy("table", "return table", true);
|
||||
}
|
||||
lua_setfield(L, -2, "__index");
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
|
||||
} // namespace nf7::luajit
|
@ -16,10 +16,6 @@ namespace nf7::luajit {
|
||||
constexpr size_t kInstructionLimit = 100000;
|
||||
|
||||
|
||||
// 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);
|
||||
|
||||
@ -37,10 +33,8 @@ void Thread::Resume(lua_State* L, int narg) noexcept {
|
||||
assert(state_ == kPaused);
|
||||
|
||||
// set global table
|
||||
PushGlobalTable(L);
|
||||
NewUserData<std::weak_ptr<Thread>>(L, weak_from_this());
|
||||
PushMeta(L);
|
||||
lua_setmetatable(L, -2);
|
||||
Push(L, GlobalTable {});
|
||||
Push(L, weak_from_this());
|
||||
lua_setfield(L, -2, "nf7");
|
||||
lua_pop(L, 1);
|
||||
|
||||
@ -102,7 +96,7 @@ Thread::Handler Thread::CreateNodeLambdaHandler(
|
||||
th.ExecResume(L);
|
||||
return;
|
||||
case 2:
|
||||
if (auto v = nf7::luajit::ToValue(L, 2)) {
|
||||
if (auto v = nf7::luajit::Peek<nf7::Value>(L, 2)) {
|
||||
auto k = luaL_checkstring(L, 1);
|
||||
caller->env().ExecSub(
|
||||
caller, [caller, callee, k = std::string {k}, v = std::move(v)]() {
|
||||
@ -134,10 +128,10 @@ Thread::Handler Thread::CreateNodeLambdaHandler(
|
||||
}
|
||||
|
||||
|
||||
static void PushMeta(lua_State* L) noexcept {
|
||||
if (luaL_newmetatable(L, Thread::kTypeName)) {
|
||||
template <> void PushMeta<std::weak_ptr<Thread>>(lua_State* L) noexcept {
|
||||
if (luaL_newmetatable(L, MetaName<std::weak_ptr<Thread>>::kValue)) {
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
CheckRef<std::weak_ptr<Thread>>(L, 1, Thread::kTypeName).~weak_ptr();
|
||||
Check<std::weak_ptr<Thread>>(L, 1).~weak_ptr();
|
||||
return 0;
|
||||
});
|
||||
lua_setfield(L, -2, "__gc");
|
||||
@ -185,17 +179,6 @@ static void PushMeta(lua_State* L) noexcept {
|
||||
});
|
||||
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, 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);
|
||||
|
@ -24,11 +24,15 @@
|
||||
|
||||
namespace nf7::luajit {
|
||||
|
||||
class Thread;
|
||||
template <> struct MetaName<std::weak_ptr<Thread>> {
|
||||
static constexpr auto kValue = "std::weak_ptr<Thread>";
|
||||
};
|
||||
template <> void PushMeta<std::weak_ptr<Thread>>(lua_State*) noexcept;
|
||||
|
||||
class Thread final : public nf7::Context,
|
||||
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*)>;
|
||||
|
||||
@ -51,7 +55,7 @@ class Thread final : public nf7::Context,
|
||||
|
||||
// must be called on luajit thread
|
||||
static std::shared_ptr<Thread> GetPtr(lua_State* L, int idx) {
|
||||
auto th = CheckRef<std::weak_ptr<Thread>>(L, idx, kTypeName).lock();
|
||||
auto th = Check<std::weak_ptr<Thread>>(L, idx).lock();
|
||||
if (th) {
|
||||
th->EnsureActive(L);
|
||||
return th;
|
||||
|
217
common/luajit_value.cc
Normal file
217
common/luajit_value.cc
Normal file
@ -0,0 +1,217 @@
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <lua.hpp>
|
||||
|
||||
#include "common/luajit.hh"
|
||||
#include "common/value.hh"
|
||||
|
||||
|
||||
namespace nf7::luajit {
|
||||
|
||||
static auto SwitchByNumericTypeName(std::string_view type, auto func) {
|
||||
if (type == "u8") { uint8_t t = 0; return func(t); }
|
||||
if (type == "u16") { uint16_t t = 0; return func(t); }
|
||||
if (type == "u32") { uint32_t t = 0; return func(t); }
|
||||
if (type == "u64") { uint64_t t = 0; return func(t); }
|
||||
if (type == "s8") { int8_t t = 0; return func(t); }
|
||||
if (type == "s16") { int16_t t = 0; return func(t); }
|
||||
if (type == "s32") { int32_t t = 0; return func(t); }
|
||||
if (type == "s64") { int64_t t = 0; return func(t); }
|
||||
if (type == "f32") { float t = 0; return func(t); }
|
||||
if (type == "f64") { double t = 0; return func(t); }
|
||||
throw nf7::Exception {"unknown numeric type name: "+std::string {type}};
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
||||
template <> void PushMeta<nf7::Value>(lua_State* L) noexcept {
|
||||
if (luaL_newmetatable(L, MetaName<nf7::Value>::kValue)) {
|
||||
lua_createtable(L, 0, 0);
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
const auto& v = Check<nf7::Value>(L, 1);
|
||||
lua_pushstring(L, v.typeName());
|
||||
return 1;
|
||||
});
|
||||
lua_setfield(L, -2, "type");
|
||||
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
const auto& v = Check<nf7::Value>(L, 1);
|
||||
std::visit([L](auto& v) { luajit::Push(L, v); }, v.value());
|
||||
return 1;
|
||||
});
|
||||
lua_setfield(L, -2, "value");
|
||||
lua_setfield(L, -2, "__index");
|
||||
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
Check<nf7::Value>(L, 1).~Value();
|
||||
return 0;
|
||||
});
|
||||
lua_setfield(L, -2, "__gc");
|
||||
}
|
||||
}
|
||||
|
||||
template <> void PushMeta<nf7::Value::Buffer>(lua_State* L) noexcept {
|
||||
if (luaL_newmetatable(L, MetaName<nf7::Value::Buffer>::kValue)) {
|
||||
lua_createtable(L, 0, 0);
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
const auto& v = Check<nf7::Value::Buffer>(L, 1);
|
||||
|
||||
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.ptr() + offset;
|
||||
const uint8_t* end = v.ptr() + v.size();
|
||||
|
||||
const auto top = lua_gettop(L);
|
||||
for (int i = 3; i <= top; ++i) {
|
||||
const std::string_view type = luaL_checkstring(L, i);
|
||||
|
||||
std::optional<size_t> n;
|
||||
if (lua_isnumber(L, i+1)) {
|
||||
const auto ni = lua_tointeger(L, i+1);
|
||||
if (ni < 0) {
|
||||
return luaL_error(L, "negative size");
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
SwitchByNumericTypeName(type, [&](auto t) {
|
||||
if (n) {
|
||||
ptr += PushArrayFromBytes<decltype(t)>(L, *n, ptr, end);
|
||||
} else {
|
||||
ptr += PushFromBytes<decltype(t)>(L, ptr, end);
|
||||
}
|
||||
});
|
||||
}
|
||||
return top-2;
|
||||
});
|
||||
lua_setfield(L, -2, "get");
|
||||
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
const auto& v = Check<nf7::Value::Buffer>(L, 1);
|
||||
lua_pushlstring(L, reinterpret_cast<const char*>(v.ptr()), v.size());
|
||||
return 1;
|
||||
});
|
||||
lua_setfield(L, -2, "str");
|
||||
|
||||
lua_pushcfunction(L, [](auto L) {
|
||||
const auto& v = Check<nf7::Value::Buffer>(L, 1);
|
||||
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) {
|
||||
Check<nf7::Value::Buffer>(L, 1).~Buffer();
|
||||
return 0;
|
||||
});
|
||||
lua_setfield(L, -2, "__gc");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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_v<T>) {
|
||||
lua_pushinteger(L, static_cast<lua_Integer>(*reinterpret_cast<const T*>(ptr)));
|
||||
} else if constexpr (std::is_floating_point_v<T>) {
|
||||
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_v<T>) {
|
||||
lua_pushinteger(L, static_cast<lua_Integer>(*reinterpret_cast<const T*>(ptr)));
|
||||
} else if constexpr (std::is_floating_point_v<T>) {
|
||||
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
|
@ -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
|
399
common/value.hh
399
common/value.hh
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@ -8,7 +9,6 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include <yas/serialize.hpp>
|
||||
#include <yas/types/std/pair.hpp>
|
||||
@ -27,145 +27,156 @@ namespace nf7 {
|
||||
|
||||
class Value {
|
||||
public:
|
||||
class IncompatibleException : public nf7::Exception {
|
||||
class Exception : public nf7::Exception {
|
||||
public:
|
||||
using nf7::Exception::Exception;
|
||||
};
|
||||
|
||||
class Data;
|
||||
using TuplePair = std::pair<std::string, nf7::Value>;
|
||||
using Boolean = bool;
|
||||
using Integer = int64_t;
|
||||
using Scalar = double;
|
||||
using String = std::string;
|
||||
|
||||
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>;
|
||||
struct Pulse { };
|
||||
struct Buffer {
|
||||
public:
|
||||
Buffer() noexcept : Buffer(nullptr, 0) {
|
||||
}
|
||||
Buffer(std::shared_ptr<const uint8_t[]> p, size_t s, size_t o = 0) noexcept :
|
||||
ptr_(p), size_(s), offset_(o) {
|
||||
assert(ptr_);
|
||||
}
|
||||
Buffer(const Buffer& src, size_t s, size_t o = 0) noexcept :
|
||||
Buffer(src.ptr_, s, src.offset_+o) {
|
||||
assert(s+o <= src.size_);
|
||||
}
|
||||
Buffer(const Buffer&) = default;
|
||||
Buffer(Buffer&&) = default;
|
||||
Buffer& operator=(const Buffer&) = default;
|
||||
Buffer& operator=(Buffer&&) = default;
|
||||
|
||||
using ConstVector = std::shared_ptr<const std::vector<uint8_t>>;
|
||||
using ConstTuple = std::shared_ptr<const std::vector<TuplePair>>;
|
||||
const uint8_t& operator[](size_t idx) const noexcept {
|
||||
return ptr_[static_cast<std::ptrdiff_t>(offset_ + idx)];
|
||||
}
|
||||
|
||||
template <typename T = uint8_t>
|
||||
const T* ptr() const noexcept {
|
||||
return reinterpret_cast<const T*>(ptr_.get()+offset_);
|
||||
}
|
||||
template <typename T = uint8_t>
|
||||
size_t size() const noexcept {
|
||||
return size_/sizeof(T);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<const uint8_t[]> ptr_;
|
||||
size_t size_ = 0;
|
||||
size_t offset_ = 0;
|
||||
};
|
||||
struct Tuple {
|
||||
public:
|
||||
using Pair = std::pair<std::string, nf7::Value>;
|
||||
struct Factory;
|
||||
|
||||
Tuple() = delete;
|
||||
Tuple(const Tuple&) = default;
|
||||
Tuple(Tuple&&) = default;
|
||||
Tuple& operator=(const Tuple&) = default;
|
||||
Tuple& operator=(Tuple&&) = default;
|
||||
|
||||
const nf7::Value& operator[](std::string_view name) const {
|
||||
for (size_t i = 0; i < size_; ++i) {
|
||||
const auto si = static_cast<std::ptrdiff_t>(i);
|
||||
if (fields_[si].first == name) {
|
||||
return fields_[si].second;
|
||||
}
|
||||
}
|
||||
throw Exception {"missing tuple field: "+std::string {name}};
|
||||
}
|
||||
const nf7::Value& operator[](size_t idx) const {
|
||||
if (idx < size_) {
|
||||
return fields_[static_cast<std::ptrdiff_t>(idx)].second;
|
||||
}
|
||||
throw Exception {"tuple index overflow"};
|
||||
}
|
||||
|
||||
std::span<const Pair> fields() const noexcept {
|
||||
auto ptr = fields_.get();
|
||||
return {ptr, ptr+size_};
|
||||
}
|
||||
size_t size() const noexcept { return size_; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<const Pair[]> fields_;
|
||||
size_t size_;
|
||||
|
||||
Tuple(std::shared_ptr<const Pair[]>&& f, size_t n) noexcept :
|
||||
fields_(std::move(f)), size_(n) {
|
||||
}
|
||||
};
|
||||
|
||||
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? v: std::make_shared<std::vector<uint8_t>>()) { }
|
||||
Value& operator=(const Vector& v) noexcept { value_ = v? v: std::make_shared<std::vector<uint8_t>>(); return *this; }
|
||||
Value(const ConstVector& v) noexcept : value_(v? std::const_pointer_cast<std::vector<uint8_t>>(v): std::make_shared<std::vector<uint8_t>>()) { }
|
||||
Value& operator=(const ConstVector& v) noexcept { value_ = v? std::const_pointer_cast<std::vector<uint8_t>>(v): std::make_shared<std::vector<uint8_t>>(); 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? v: std::make_shared<std::vector<TuplePair>>()) { }
|
||||
Value& operator=(const Tuple& v) noexcept { value_ = v? v: std::make_shared<std::vector<TuplePair>>(); return *this; }
|
||||
Value(const ConstTuple& v) noexcept : value_(v? v: std::make_shared<std::vector<TuplePair>>()) { }
|
||||
Value& operator=(const ConstTuple& v) noexcept { value_ = v? v: std::make_shared<std::vector<TuplePair>>(); 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 { *this = std::move(v); }
|
||||
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; }
|
||||
Value(const auto& v) noexcept : value_(AssignCast(v)) { }
|
||||
Value(auto&& v) noexcept : value_(AssignCast(std::move(v))) { }
|
||||
Value& operator=(const auto& v) noexcept { value_ = AssignCast(v); return *this; }
|
||||
Value& operator=(auto&& v) noexcept { value_ = AssignCast(std::move(v)); return *this; }
|
||||
|
||||
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<ConstVector>(value_); }
|
||||
bool isTuple() const noexcept { return std::holds_alternative<ConstTuple>(value_); }
|
||||
bool isData() const noexcept { return std::holds_alternative<DataPtr>(value_); }
|
||||
bool isBuffer() const noexcept { return std::holds_alternative<Buffer>(value_); }
|
||||
bool isTuple() const noexcept { return std::holds_alternative<Tuple>(value_); }
|
||||
|
||||
// direct accessors
|
||||
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<ConstVector>(); }
|
||||
const ConstTuple& tuple() const { return get<ConstTuple>(); }
|
||||
const DataPtr& data() const { return get<DataPtr>(); }
|
||||
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 Buffer& vector() const { return get<Buffer>(); }
|
||||
const Tuple& tuple() const { return get<Tuple>(); }
|
||||
const auto& value() const noexcept { return value_; }
|
||||
|
||||
// direct reference accessor
|
||||
Integer& integer() { return get<Integer>(); }
|
||||
Boolean& boolean() { return get<Boolean>(); }
|
||||
Scalar& scalar() { return get<Scalar>(); }
|
||||
String& string() { return get<String>(); }
|
||||
Scalar& scalar() { return get<Scalar>(); }
|
||||
String& string() { return get<String>(); }
|
||||
Buffer& vector() { return get<Buffer>(); }
|
||||
Tuple& tuple() { return get<Tuple>(); }
|
||||
|
||||
// conversion accessor
|
||||
template <typename N>
|
||||
N integer() const {
|
||||
return SafeCast<N>(integer());
|
||||
}
|
||||
N integer() const { return SafeCast<N>(integer()); }
|
||||
template <typename N>
|
||||
N scalar() const {
|
||||
return SafeCast<N>(scalar());
|
||||
}
|
||||
N scalar() const { return SafeCast<N>(scalar()); }
|
||||
template <typename N>
|
||||
N integerOrScalar() const {
|
||||
try {
|
||||
return SafeCast<N>(integer());
|
||||
} catch (nf7::Exception&) {
|
||||
return SafeCast<N>(scalar());
|
||||
if (isInteger()) {
|
||||
return integer<N>();
|
||||
} else if (isScalar()) {
|
||||
return scalar<N>();
|
||||
} else {
|
||||
throw Exception {"expected integer or scalar"};
|
||||
}
|
||||
}
|
||||
template <typename N>
|
||||
N scalarOrInteger() const {
|
||||
try {
|
||||
return SafeCast<N>(scalar());
|
||||
} catch (nf7::Exception&) {
|
||||
return SafeCast<N>(integer());
|
||||
if (isScalar()) {
|
||||
return scalar<N>();
|
||||
} else if (isInteger()) {
|
||||
return integer<N>();
|
||||
} else {
|
||||
throw Exception {"expected scalar or integer"};
|
||||
}
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
// tuple element accessor
|
||||
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});
|
||||
}
|
||||
Value tupleOr(auto idx, const Value& v) const noexcept {
|
||||
const Value& tuple(auto& idx) const { return tuple()[idx]; }
|
||||
const Value& tupleOr(auto idx, const Value& v) const noexcept {
|
||||
try {
|
||||
return tuple(idx);
|
||||
} catch (nf7::Exception&) {
|
||||
@ -173,40 +184,38 @@ class Value {
|
||||
}
|
||||
}
|
||||
|
||||
// extended accessor
|
||||
// meta accessor
|
||||
nf7::File& file(const nf7::File& base) const {
|
||||
if (isInteger()) {
|
||||
return base.env().GetFileOrThrow(integerOrScalar<nf7::File::Id>());
|
||||
} else if (isString()) {
|
||||
return base.ResolveOrThrow(string());
|
||||
} else {
|
||||
throw IncompatibleException {"expected file id or file path"};
|
||||
throw Exception {"expected file id or file path"};
|
||||
}
|
||||
}
|
||||
|
||||
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()(ConstVector) noexcept { return "vector"; }
|
||||
auto operator()(ConstTuple) noexcept { return "tuple"; }
|
||||
auto operator()(DataPtr) noexcept { return "data"; }
|
||||
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()(Buffer) noexcept { return "buffer"; }
|
||||
auto operator()(Tuple) noexcept { return "tuple"; }
|
||||
};
|
||||
return std::visit(Visitor {}, value_);
|
||||
}
|
||||
|
||||
template <typename Ar>
|
||||
Ar& serialize(Ar& ar) noexcept {
|
||||
auto& serialize(auto& ar) {
|
||||
ar & value_;
|
||||
return ar;
|
||||
}
|
||||
|
||||
private:
|
||||
std::variant<Pulse, Boolean, Integer, Scalar, String, ConstVector, ConstTuple, DataPtr> value_;
|
||||
std::variant<Pulse, Boolean, Integer, Scalar, String, Buffer, Tuple> value_;
|
||||
|
||||
|
||||
template <typename T>
|
||||
@ -220,7 +229,42 @@ class Value {
|
||||
} catch (std::bad_variant_access&) {
|
||||
std::stringstream st;
|
||||
st << "expected " << typeid(T).name() << " but it's " << typeName();
|
||||
throw IncompatibleException(st.str());
|
||||
throw Exception(st.str());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static auto AssignCast(T&& v) noexcept {
|
||||
if constexpr (std::is_same_v<T, nf7::Value>) {
|
||||
return std::move(v.value_);
|
||||
} else if constexpr (
|
||||
std::is_same_v<T, std::string> ||
|
||||
std::is_same_v<T, Buffer> ||
|
||||
std::is_same_v<T, Tuple>) {
|
||||
return std::move(v);
|
||||
} else {
|
||||
return AssignCast(static_cast<const T&>(v));
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
static auto AssignCast(const T& v) noexcept {
|
||||
if constexpr (std::is_same_v<T, nf7::Value>) {
|
||||
return v.value_;
|
||||
} else if constexpr (std::is_integral_v<T>) {
|
||||
return static_cast<Integer>(v);
|
||||
} else if constexpr (std::is_floating_point_v<T>) {
|
||||
return static_cast<Scalar>(v);
|
||||
} else if constexpr (std::is_same_v<T, std::string_view> ||
|
||||
std::is_same_v<T, std::string>) {
|
||||
return std::string {v};
|
||||
} else if constexpr (std::is_same_v<T, Buffer> ||
|
||||
std::is_same_v<T, Tuple>) {
|
||||
return T {v};
|
||||
} else {
|
||||
[]<bool flag = false>(){
|
||||
static_assert(flag, "no type conversion found to assign");
|
||||
}();
|
||||
return 0; // to suppress 'invalid use of void expression' warning
|
||||
}
|
||||
}
|
||||
|
||||
template <typename R, typename N>
|
||||
@ -229,31 +273,59 @@ class Value {
|
||||
const auto retn = static_cast<N>(ret);
|
||||
if constexpr (std::is_unsigned<R>::value) {
|
||||
if (in < 0) {
|
||||
throw IncompatibleException("integer underflow");
|
||||
throw Exception("integer underflow");
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_integral<R>::value && std::is_integral<N>::value) {
|
||||
if (in != retn) {
|
||||
throw IncompatibleException("integer out of range");
|
||||
throw Exception("integer out of range");
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_integral<R>::value && std::is_floating_point<N>::value) {
|
||||
if (std::max(retn, in) - std::min(retn, in) > 1) {
|
||||
throw IncompatibleException("bad precision while conversion of floating point");
|
||||
throw Exception("bad precision while conversion of floating point");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
class Value::Data {
|
||||
struct Value::Tuple::Factory final {
|
||||
public:
|
||||
Data() = default;
|
||||
virtual ~Data() = default;
|
||||
Data(const Data&) = default;
|
||||
Data(Data&&) = default;
|
||||
Data& operator=(const Data&) = default;
|
||||
Data& operator=(Data&&) = default;
|
||||
Factory() = delete;
|
||||
Factory(size_t max) noexcept :
|
||||
ptr_(std::make_unique<Pair[]>(max)), max_(max) {
|
||||
}
|
||||
Factory(const Factory&) = delete;
|
||||
Factory(Factory&&) = delete;
|
||||
Factory& operator=(const Factory&) = delete;
|
||||
Factory& operator=(Factory&&) = delete;
|
||||
|
||||
nf7::Value& operator[](std::string_view str) noexcept {
|
||||
assert(size_ < max_);
|
||||
auto& p = ptr_[size_++];
|
||||
p.first = str;
|
||||
return p.second;
|
||||
}
|
||||
|
||||
nf7::Value& Append() noexcept {
|
||||
return ptr_[size_++].second;
|
||||
}
|
||||
nf7::Value& Append(auto&& v) noexcept {
|
||||
return Append() = std::move(v);
|
||||
}
|
||||
nf7::Value& Append(const auto& v) noexcept {
|
||||
return Append() = v;
|
||||
}
|
||||
|
||||
Tuple Create() noexcept {
|
||||
return size_? Tuple {std::move(ptr_), size_}: Tuple {nullptr, 0};
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Pair[]> ptr_;
|
||||
size_t max_;
|
||||
size_t size_ = 0;
|
||||
};
|
||||
|
||||
} // namespace nf7
|
||||
@ -283,31 +355,15 @@ struct serializer<
|
||||
type_prop::not_a_fundamental,
|
||||
ser_case::use_internal_serializer,
|
||||
F,
|
||||
nf7::Value::Vector> {
|
||||
nf7::Value::Buffer> {
|
||||
public:
|
||||
template <typename Archive>
|
||||
static Archive& save(Archive&, const nf7::Value::Vector&) {
|
||||
throw nf7::Exception("cannot serialize Value::Vector");
|
||||
static Archive& save(Archive&, const nf7::Value::Buffer&) {
|
||||
throw nf7::Exception("cannot serialize Value::Buffer");
|
||||
}
|
||||
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::ConstVector> {
|
||||
public:
|
||||
template <typename Archive>
|
||||
static Archive& save(Archive&, const nf7::Value::ConstVector&) {
|
||||
throw nf7::Exception("cannot serialize Value::Vector");
|
||||
}
|
||||
template <typename Archive>
|
||||
static Archive& load(Archive&, nf7::Value::ConstVector&) {
|
||||
throw nf7::DeserializeException("cannot deserialize Value::Vector");
|
||||
static Archive& load(Archive&, nf7::Value::Buffer&) {
|
||||
throw nf7::DeserializeException("cannot deserialize Value::Buffer");
|
||||
}
|
||||
};
|
||||
|
||||
@ -319,51 +375,12 @@ struct serializer<
|
||||
nf7::Value::Tuple> {
|
||||
public:
|
||||
template <typename Archive>
|
||||
static Archive& save(Archive& ar, const nf7::Value::Tuple& tup) {
|
||||
ar(*tup);
|
||||
return ar;
|
||||
static Archive& save(Archive&, const nf7::Value::Tuple&) {
|
||||
throw nf7::Exception("cannot serialize Value::Tuple");
|
||||
}
|
||||
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::ConstTuple> {
|
||||
public:
|
||||
template <typename Archive>
|
||||
static Archive& save(Archive& ar, const nf7::Value::ConstTuple& tup) {
|
||||
ar(*tup);
|
||||
return ar;
|
||||
}
|
||||
template <typename Archive>
|
||||
static Archive& load(Archive& ar, nf7::Value::ConstTuple& tup) {
|
||||
auto ptr = std::make_shared<std::vector<nf7::Value::TuplePair>>();
|
||||
ar(*ptr);
|
||||
tup = std::move(ptr);
|
||||
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");
|
||||
static Archive& load(Archive&, nf7::Value::Tuple&) {
|
||||
throw nf7::DeserializeException("cannot deserialize Value::Tuple");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -308,18 +308,17 @@ class Device::Lambda final : public nf7::Node::Lambda,
|
||||
auto& ring = inst->ring();
|
||||
|
||||
if (in.name == "info") {
|
||||
std::vector<nf7::Value::TuplePair> tup {
|
||||
{"format", magic_enum::enum_name(data.fmt)},
|
||||
{"srate", static_cast<nf7::Value::Integer>(data.srate)},
|
||||
{"ch", static_cast<nf7::Value::Integer>(data.ch)},
|
||||
};
|
||||
in.sender->Handle("result", std::move(tup), shared_from_this());
|
||||
nf7::Value::Tuple::Factory tup {3};
|
||||
tup["format"] = magic_enum::enum_name(data.fmt);
|
||||
tup["srate"] = static_cast<nf7::Value::Integer>(data.srate);
|
||||
tup["ch"] = static_cast<nf7::Value::Integer>(data.ch);
|
||||
in.sender->Handle("result", tup.Create(), shared_from_this());
|
||||
|
||||
} else if (in.name == "mix") {
|
||||
if (data.mode != Mode::Playback) {
|
||||
throw nf7::Exception {"device mode is not playback"};
|
||||
}
|
||||
const auto& vec = *in.value.vector();
|
||||
const auto& vec = in.value.vector();
|
||||
|
||||
std::unique_lock<std::mutex> lock(inst->mtx());
|
||||
if (reset) time_ = ring.cur();
|
||||
@ -327,7 +326,7 @@ class Device::Lambda final : public nf7::Node::Lambda,
|
||||
|
||||
const auto Mix = [&]<typename T>() {
|
||||
time_ = ring.Mix(
|
||||
time_, reinterpret_cast<const T*>(vec.data()), vec.size()/sizeof(T));
|
||||
time_, reinterpret_cast<const T*>(vec.ptr()), vec.size()/sizeof(T));
|
||||
};
|
||||
switch (data.fmt) {
|
||||
case Format::U8 : Mix.operator()<uint8_t>(); break;
|
||||
|
@ -194,24 +194,29 @@ class FontFace::Lambda final : public nf7::Node::Lambda,
|
||||
}
|
||||
|
||||
// copy rendered bitmap
|
||||
std::vector<uint8_t> dst(g.bitmap.width*g.bitmap.rows);
|
||||
const auto dstn = g.bitmap.width*g.bitmap.rows;
|
||||
const auto dstp = std::make_shared<uint8_t[]>(dstn);
|
||||
|
||||
auto dst = dstp.get();
|
||||
auto src = g.bitmap.buffer;
|
||||
for (unsigned int y = 0; y < g.bitmap.rows; ++y) {
|
||||
std::memcpy(&dst[y*g.bitmap.width], src, g.bitmap.width);
|
||||
std::memcpy(dst, src, g.bitmap.width);
|
||||
dst += g.bitmap.width;
|
||||
src += g.bitmap.pitch;
|
||||
}
|
||||
|
||||
return nf7::Value { std::vector<nf7::Value::TuplePair> {
|
||||
{"w", static_cast<nf7::Value::Integer>(g.bitmap.width)},
|
||||
{"h", static_cast<nf7::Value::Integer>(g.bitmap.rows)},
|
||||
{"buf", std::move(dst)},
|
||||
{"hBearX", static_cast<nf7::Value::Scalar>(g.metrics.horiBearingX)/64},
|
||||
{"hBearY", static_cast<nf7::Value::Scalar>(g.metrics.horiBearingY)/64},
|
||||
{"hAdv", static_cast<nf7::Value::Scalar>(g.metrics.horiAdvance)/64},
|
||||
{"vBearX", static_cast<nf7::Value::Scalar>(g.metrics.vertBearingX)/64},
|
||||
{"vBearY", static_cast<nf7::Value::Scalar>(g.metrics.vertBearingY)/64},
|
||||
{"vAdv", static_cast<nf7::Value::Scalar>(g.metrics.vertAdvance)/64},
|
||||
}};
|
||||
// create tuple and return result
|
||||
nf7::Value::Tuple::Factory tup {9};
|
||||
tup["w"] = static_cast<nf7::Value::Integer>(g.bitmap.width);
|
||||
tup["h"] = static_cast<nf7::Value::Integer>(g.bitmap.rows);
|
||||
tup["buf"] = nf7::Value::Buffer(std::move(dstp), dstn);
|
||||
tup["hBearX"] = static_cast<nf7::Value::Scalar>(g.metrics.horiBearingX)/64;
|
||||
tup["hBearY"] = static_cast<nf7::Value::Scalar>(g.metrics.horiBearingY)/64;
|
||||
tup["hAdv"] = static_cast<nf7::Value::Scalar>(g.metrics.horiAdvance)/64;
|
||||
tup["vBearX"] = static_cast<nf7::Value::Scalar>(g.metrics.vertBearingX)/64;
|
||||
tup["vBearY"] = static_cast<nf7::Value::Scalar>(g.metrics.vertBearingY)/64;
|
||||
tup["vAdv"] = static_cast<nf7::Value::Scalar>(g.metrics.vertAdvance)/64;
|
||||
return nf7::Value { tup.Create() };
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -1,397 +0,0 @@
|
||||
#include "nf7.hh"
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <imgui_stdlib.h>
|
||||
|
||||
#include <yas/types/std/string.hpp>
|
||||
#include <yas/types/std/vector.hpp>
|
||||
|
||||
#include "common/file_base.hh"
|
||||
#include "common/generic_context.hh"
|
||||
#include "common/generic_memento.hh"
|
||||
#include "common/generic_type_info.hh"
|
||||
#include "common/gui.hh"
|
||||
#include "common/gui_value.hh"
|
||||
#include "common/life.hh"
|
||||
#include "common/ptr_selector.hh"
|
||||
#include "common/sequencer.hh"
|
||||
#include "common/value.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
namespace {
|
||||
|
||||
class Adaptor final : public nf7::FileBase,
|
||||
public nf7::Sequencer {
|
||||
public:
|
||||
static inline const nf7::GenericTypeInfo<Adaptor> kType = {
|
||||
"Sequencer/Adaptor", {"nf7::Sequencer"},
|
||||
"wraps and adapts other Sequencer",
|
||||
};
|
||||
|
||||
class Session;
|
||||
class Lambda;
|
||||
class Editor;
|
||||
|
||||
struct Var {
|
||||
std::string name;
|
||||
bool peek = false;
|
||||
|
||||
void serialize(auto& ar) {
|
||||
ar(name, peek);
|
||||
}
|
||||
};
|
||||
struct Data {
|
||||
nf7::File::Path path;
|
||||
std::vector<std::pair<std::string, nf7::gui::Value>> input_imm;
|
||||
std::vector<std::pair<std::string, Var>> input_map;
|
||||
std::vector<std::pair<std::string, std::string>> output_map;
|
||||
|
||||
void serialize(auto& ar) {
|
||||
ar(path, input_imm, input_map, output_map);
|
||||
}
|
||||
};
|
||||
|
||||
Adaptor(nf7::Env& env, Data&& d = {}) noexcept :
|
||||
nf7::FileBase(kType, env),
|
||||
nf7::Sequencer(Sequencer::kCustomItem |
|
||||
Sequencer::kTooltip |
|
||||
Sequencer::kParamPanel),
|
||||
life_(*this), mem_(*this, std::move(d)) {
|
||||
}
|
||||
|
||||
Adaptor(nf7::Deserializer& ar) : Adaptor(ar.env()) {
|
||||
ar(mem_.data());
|
||||
}
|
||||
void Serialize(nf7::Serializer& ar) const noexcept override {
|
||||
ar(mem_.data());
|
||||
}
|
||||
std::unique_ptr<nf7::File> Clone(nf7::Env& env) const noexcept override {
|
||||
return std::make_unique<Adaptor>(env, Data {mem_.data()});
|
||||
}
|
||||
|
||||
std::shared_ptr<nf7::Sequencer::Lambda> CreateLambda(
|
||||
const std::shared_ptr<nf7::Context>&) noexcept override;
|
||||
|
||||
void UpdateItem(nf7::Sequencer::Editor&) noexcept override;
|
||||
void UpdateParamPanel(nf7::Sequencer::Editor&) noexcept override;
|
||||
void UpdateTooltip(nf7::Sequencer::Editor&) noexcept override;
|
||||
|
||||
nf7::File::Interface* interface(const std::type_info& t) noexcept override {
|
||||
return nf7::InterfaceSelector<
|
||||
nf7::Memento, nf7::Sequencer>(t).Select(this, &mem_);
|
||||
}
|
||||
|
||||
private:
|
||||
nf7::Life<Adaptor> life_;
|
||||
|
||||
nf7::GenericMemento<Data> mem_;
|
||||
};
|
||||
|
||||
|
||||
class Adaptor::Session final : public nf7::Sequencer::Session {
|
||||
public:
|
||||
// ensure that Adaptor is alive
|
||||
Session(Adaptor& f, const std::shared_ptr<nf7::Sequencer::Session>& parent) noexcept : parent_(parent) {
|
||||
for (auto& p : f.mem_->input_imm) {
|
||||
vars_[p.first] = p.second.entity();
|
||||
}
|
||||
for (auto& p : f.mem_->input_map) {
|
||||
if (p.second.name.size() == 0) continue;
|
||||
if (p.second.peek) {
|
||||
if (const auto ptr = parent->Peek(p.second.name)) {
|
||||
vars_[p.first] = *ptr;
|
||||
}
|
||||
} else {
|
||||
if (auto ptr = parent->Receive(p.second.name)) {
|
||||
vars_[p.first] = std::move(*ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto& p : f.mem_->output_map) {
|
||||
outs_[p.first] = p.second;
|
||||
}
|
||||
}
|
||||
|
||||
const nf7::Value* Peek(std::string_view name) noexcept override {
|
||||
auto itr = vars_.find(std::string {name});
|
||||
return itr != vars_.end()? &itr->second: nullptr;
|
||||
}
|
||||
std::optional<nf7::Value> Receive(std::string_view name) noexcept override {
|
||||
auto itr = vars_.find(std::string {name});
|
||||
if (itr == vars_.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
auto ret = std::move(itr->second);
|
||||
vars_.erase(itr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Send(std::string_view name, nf7::Value&& v) noexcept override {
|
||||
if (done_) return;
|
||||
|
||||
auto itr = outs_.find(std::string {name});
|
||||
if (itr != outs_.end()) {
|
||||
parent_->Send(itr->second, std::move(v));
|
||||
}
|
||||
}
|
||||
|
||||
void Finish() noexcept override {
|
||||
assert(parent_);
|
||||
parent_->Finish();
|
||||
done_ = true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<nf7::Sequencer::Session> parent_;
|
||||
|
||||
std::unordered_map<std::string, nf7::Value> vars_;
|
||||
std::unordered_map<std::string, std::string> outs_;
|
||||
|
||||
bool done_ = false;
|
||||
};
|
||||
class Adaptor::Lambda final : public nf7::Sequencer::Lambda,
|
||||
public std::enable_shared_from_this<Adaptor::Lambda> {
|
||||
public:
|
||||
Lambda(Adaptor& f, const std::shared_ptr<nf7::Context>& parent) noexcept :
|
||||
nf7::Sequencer::Lambda(f, parent), f_(f.life_) {
|
||||
}
|
||||
|
||||
void Run(const std::shared_ptr<nf7::Sequencer::Session>& ss) noexcept override
|
||||
try {
|
||||
f_.EnforceAlive();
|
||||
|
||||
auto& target = f_->ResolveOrThrow(f_->mem_->path);
|
||||
auto& seq = target.interfaceOrThrow<nf7::Sequencer>();
|
||||
if (!la_ || target.id() != cached_id_) {
|
||||
la_ = seq.CreateLambda(shared_from_this());
|
||||
cached_id_ = target.id();
|
||||
}
|
||||
la_->Run(std::make_shared<Adaptor::Session>(*f_, ss));
|
||||
} catch (nf7::Exception&) {
|
||||
ss->Finish();
|
||||
}
|
||||
|
||||
private:
|
||||
nf7::Life<Adaptor>::Ref f_;
|
||||
|
||||
nf7::File::Id cached_id_ = 0;
|
||||
std::shared_ptr<nf7::Sequencer::Lambda> la_;
|
||||
};
|
||||
std::shared_ptr<nf7::Sequencer::Lambda> Adaptor::CreateLambda(
|
||||
const std::shared_ptr<nf7::Context>& parent) noexcept {
|
||||
return std::make_shared<Adaptor::Lambda>(*this, parent);
|
||||
}
|
||||
|
||||
|
||||
class Adaptor::Editor final : public nf7::Sequencer::Editor {
|
||||
public:
|
||||
using nf7::Sequencer::Editor::Editor;
|
||||
};
|
||||
|
||||
|
||||
void Adaptor::UpdateItem(Sequencer::Editor&) noexcept {
|
||||
try {
|
||||
auto& seq = ResolveOrThrow(mem_->path).interfaceOrThrow<nf7::Sequencer>();
|
||||
if (seq.flags() & nf7::Sequencer::kCustomItem) {
|
||||
Adaptor::Editor ed;
|
||||
seq.UpdateItem(ed);
|
||||
}
|
||||
} catch (nf7::File::NotFoundException&) {
|
||||
ImGui::TextUnformatted("file missing");
|
||||
} catch (nf7::File::NotImplementedException&) {
|
||||
ImGui::TextUnformatted("file does not have Sequencer interface");
|
||||
}
|
||||
}
|
||||
void Adaptor::UpdateParamPanel(Sequencer::Editor&) noexcept {
|
||||
bool commit = false;
|
||||
|
||||
auto& imm = mem_->input_imm;
|
||||
auto& inputs = mem_->input_map;
|
||||
auto& outputs = mem_->output_map;
|
||||
|
||||
const auto em = ImGui::GetFontSize();
|
||||
if (ImGui::CollapsingHeader("Sequencer/Adaptor", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
if (nf7::gui::PathButton("path", mem_->path, *this)) {
|
||||
commit = true;
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("table", 3)) {
|
||||
ImGui::TableSetupColumn("left", ImGuiTableColumnFlags_WidthStretch, 1.f);
|
||||
ImGui::TableSetupColumn("arrow", ImGuiTableColumnFlags_WidthFixed, 1*em);
|
||||
ImGui::TableSetupColumn("right", ImGuiTableColumnFlags_WidthStretch, 1.f);
|
||||
|
||||
// ---- immediate values
|
||||
ImGui::PushID("imm");
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Spacing();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted("imm input");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("+")) {
|
||||
imm.push_back({"target_input", {}});
|
||||
commit = true;
|
||||
}
|
||||
if (imm.size() == 0) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextDisabled("no rule");
|
||||
}
|
||||
for (size_t i = 0; i < imm.size(); ++i) {
|
||||
auto& p = imm[i];
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::PushID(static_cast<int>(i));
|
||||
|
||||
if (ImGui::TableNextColumn()) {
|
||||
commit |= p.second.UpdateTypeButton("T");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
commit |= p.second.UpdateEditor();
|
||||
}
|
||||
if (ImGui::TableNextColumn()) {
|
||||
ImGui::TextUnformatted("->");
|
||||
}
|
||||
if (ImGui::TableNextColumn()) {
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
ImGui::InputTextWithHint("##name", "dst", &p.first);
|
||||
commit |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::PopID();
|
||||
|
||||
// ---- input map
|
||||
ImGui::PushID("input");
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Spacing();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted("input");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("+")) {
|
||||
inputs.push_back({"target_input", {}});
|
||||
commit = true;
|
||||
}
|
||||
if (inputs.size() == 0) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextDisabled("no rule");
|
||||
}
|
||||
for (size_t i = 0; i < inputs.size(); ++i) {
|
||||
auto& in = inputs[i];
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::PushID(static_cast<int>(i));
|
||||
|
||||
if (ImGui::TableNextColumn()) {
|
||||
const char* text = in.second.peek? "P": "R";
|
||||
if (ImGui::Button(text)) {
|
||||
in.second.peek = !in.second.peek;
|
||||
commit = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
ImGui::InputTextWithHint("##src", "src", &in.second.name);
|
||||
commit |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
}
|
||||
if (ImGui::TableNextColumn()) {
|
||||
ImGui::TextUnformatted("->");
|
||||
}
|
||||
if (ImGui::TableNextColumn()) {
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
ImGui::InputTextWithHint("##dst", "dst", &in.first);
|
||||
commit |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::PopID();
|
||||
|
||||
// ---- output map
|
||||
ImGui::PushID("output");
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Spacing();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted("output");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("+")) {
|
||||
outputs.push_back({"target_output", ""});
|
||||
commit = true;
|
||||
}
|
||||
if (outputs.size() == 0) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextDisabled("no rule");
|
||||
}
|
||||
for (size_t i = 0; i < outputs.size(); ++i) {
|
||||
auto& out = outputs[i];
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::PushID(static_cast<int>(i));
|
||||
|
||||
if (ImGui::TableNextColumn()) {
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
ImGui::InputTextWithHint("##src", "src", &out.first);
|
||||
commit |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
}
|
||||
if (ImGui::TableNextColumn()) {
|
||||
ImGui::TextUnformatted("->");
|
||||
}
|
||||
if (ImGui::TableNextColumn()) {
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
ImGui::InputTextWithHint("##dst", "dst", &out.second);
|
||||
commit |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::PopID();
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
if (commit) {
|
||||
imm.erase(
|
||||
std::remove_if(
|
||||
imm.begin(), imm.end(),
|
||||
[](auto& x) { return x.first.size() == 0; }),
|
||||
imm.end());
|
||||
inputs.erase(
|
||||
std::remove_if(
|
||||
inputs.begin(), inputs.end(),
|
||||
[](auto& x) { return x.first.size() == 0; }),
|
||||
inputs.end());
|
||||
outputs.erase(
|
||||
std::remove_if(
|
||||
outputs.begin(), outputs.end(),
|
||||
[](auto& x) { return x.first.size() == 0; }),
|
||||
outputs.end());
|
||||
mem_.Commit();
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
try {
|
||||
auto& seq = ResolveOrThrow(mem_->path).interfaceOrThrow<nf7::Sequencer>();
|
||||
if (seq.flags() & nf7::Sequencer::kParamPanel) {
|
||||
Adaptor::Editor ed;
|
||||
seq.UpdateParamPanel(ed);
|
||||
}
|
||||
} catch (nf7::Exception&) {
|
||||
ImGui::Separator();
|
||||
ImGui::TextUnformatted("TARGET HAS NO SEQUENCER INTERFACE");
|
||||
}
|
||||
}
|
||||
void Adaptor::UpdateTooltip(Sequencer::Editor&) noexcept {
|
||||
ImGui::TextUnformatted("Sequencer/Adaptor");
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nf7
|
@ -1,241 +0,0 @@
|
||||
#include "nf7.hh"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <imgui_stdlib.h>
|
||||
|
||||
#include <yas/types/std/string.hpp>
|
||||
#include <yas/types/std/vector.hpp>
|
||||
|
||||
#include "common/file_base.hh"
|
||||
#include "common/gui.hh"
|
||||
#include "common/generic_context.hh"
|
||||
#include "common/generic_memento.hh"
|
||||
#include "common/generic_type_info.hh"
|
||||
#include "common/life.hh"
|
||||
#include "common/node.hh"
|
||||
#include "common/ptr_selector.hh"
|
||||
#include "common/sequencer.hh"
|
||||
|
||||
|
||||
namespace nf7 {
|
||||
namespace {
|
||||
|
||||
class Call final : public nf7::FileBase, public nf7::Sequencer {
|
||||
public:
|
||||
static inline const nf7::GenericTypeInfo<Call> kType = {
|
||||
"Sequencer/Call", {"nf7::Sequencer"},
|
||||
"calls an external Node as a Sequencer",
|
||||
};
|
||||
|
||||
class Lambda;
|
||||
class SessionLambda;
|
||||
|
||||
struct Data {
|
||||
nf7::File::Path callee;
|
||||
std::string expects;
|
||||
bool pure;
|
||||
|
||||
void serialize(auto& ar) {
|
||||
ar(callee, expects, pure);
|
||||
}
|
||||
};
|
||||
|
||||
Call(nf7::Env& env, Data&& data = {}) noexcept :
|
||||
nf7::FileBase(kType, env),
|
||||
Sequencer(Sequencer::kCustomItem |
|
||||
Sequencer::kTooltip |
|
||||
Sequencer::kParamPanel),
|
||||
life_(*this),
|
||||
mem_(*this, std::move(data)) {
|
||||
}
|
||||
|
||||
Call(nf7::Deserializer& ar) : Call(ar.env()) {
|
||||
ar(mem_.data());
|
||||
}
|
||||
void Serialize(nf7::Serializer& ar) const noexcept override {
|
||||
ar(mem_.data());
|
||||
}
|
||||
std::unique_ptr<nf7::File> Clone(nf7::Env& env) const noexcept override {
|
||||
return std::make_unique<Call>(env, Data {mem_.data()});
|
||||
}
|
||||
|
||||
std::shared_ptr<nf7::Sequencer::Lambda> CreateLambda(
|
||||
const std::shared_ptr<nf7::Context>&) noexcept override;
|
||||
|
||||
void UpdateItem(nf7::Sequencer::Editor&) noexcept override;
|
||||
void UpdateParamPanel(nf7::Sequencer::Editor&) noexcept override;
|
||||
void UpdateTooltip(nf7::Sequencer::Editor&) noexcept override;
|
||||
|
||||
nf7::File::Interface* interface(const std::type_info& t) noexcept override {
|
||||
return nf7::InterfaceSelector<
|
||||
nf7::Memento, nf7::Sequencer>(t).Select(this, &mem_);
|
||||
}
|
||||
|
||||
private:
|
||||
nf7::Life<Call> life_;
|
||||
|
||||
nf7::GenericMemento<Data> mem_;
|
||||
};
|
||||
|
||||
|
||||
class Call::Lambda final : public nf7::Sequencer::Lambda,
|
||||
public std::enable_shared_from_this<Call::Lambda> {
|
||||
public:
|
||||
Lambda(Call& f, const std::shared_ptr<nf7::Context>& ctx) noexcept :
|
||||
Sequencer::Lambda(f, ctx), file_(f.life_) {
|
||||
}
|
||||
|
||||
void Run(const std::shared_ptr<Sequencer::Session>& ss) noexcept override;
|
||||
void Abort() noexcept override;
|
||||
|
||||
private:
|
||||
nf7::Life<Call>::Ref file_;
|
||||
|
||||
std::shared_ptr<Call::SessionLambda> ssla_;
|
||||
|
||||
nf7::Node* cached_node_ = nullptr;
|
||||
std::shared_ptr<Node::Lambda> la_;
|
||||
|
||||
bool abort_ = false;
|
||||
};
|
||||
class Call::SessionLambda final : public nf7::Node::Lambda {
|
||||
public:
|
||||
SessionLambda(Call& f, const std::shared_ptr<Call::Lambda>& parent) noexcept :
|
||||
nf7::Node::Lambda(f, parent) {
|
||||
}
|
||||
|
||||
void Listen(Call& f, const std::shared_ptr<Sequencer::Session>& ss) noexcept {
|
||||
assert(!ss_);
|
||||
ss_ = ss;
|
||||
|
||||
const auto ex = f.mem_->expects;
|
||||
size_t begin = 0;
|
||||
for (size_t i = 0; i <= ex.size(); ++i) {
|
||||
if (i == ex.size() || ex[i] == '\n') {
|
||||
auto name = ex.substr(begin, i-begin);
|
||||
if (name.size() > 0) {
|
||||
expects_.insert(std::move(name));
|
||||
}
|
||||
begin = i+1;
|
||||
}
|
||||
}
|
||||
FinishIf();
|
||||
}
|
||||
void Handle(const nf7::Node::Lambda::Msg& in) noexcept override {
|
||||
if (!ss_) return;
|
||||
ss_->Send(in.name, nf7::Value {in.value});
|
||||
expects_.erase(in.name);
|
||||
FinishIf();
|
||||
}
|
||||
void Abort() noexcept override {
|
||||
if (ss_) {
|
||||
ss_->Finish();
|
||||
ss_ = nullptr;
|
||||
expects_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Sequencer::Session> ss_;
|
||||
|
||||
std::unordered_set<std::string> expects_;
|
||||
|
||||
void FinishIf() noexcept {
|
||||
if (expects_.size() == 0) {
|
||||
ss_->Finish();
|
||||
ss_ = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
std::shared_ptr<Sequencer::Lambda> Call::CreateLambda(
|
||||
const std::shared_ptr<nf7::Context>& parent) noexcept {
|
||||
return std::make_shared<Call::Lambda>(*this, parent);
|
||||
}
|
||||
void Call::Lambda::Run(const std::shared_ptr<Sequencer::Session>& ss) noexcept
|
||||
try {
|
||||
if (abort_) return;
|
||||
file_.EnforceAlive();
|
||||
|
||||
auto& data = file_->mem_.data();
|
||||
auto& callee = file_->ResolveOrThrow(data.callee);
|
||||
auto& node = callee.interfaceOrThrow<nf7::Node>();
|
||||
|
||||
if (!ssla_) {
|
||||
ssla_ = std::make_shared<Call::SessionLambda>(*file_, shared_from_this());
|
||||
}
|
||||
|
||||
auto self = shared_from_this();
|
||||
if (!la_ || &node != std::exchange(cached_node_, &node)) {
|
||||
la_ = node.CreateLambda(ssla_);
|
||||
}
|
||||
|
||||
ssla_->Listen(*file_, ss);
|
||||
const auto inputs = node.GetMeta().inputs;
|
||||
for (const auto& name : inputs) {
|
||||
if (auto v = ss->Receive(name)) {
|
||||
la_->Handle(name, *v, ssla_);
|
||||
}
|
||||
}
|
||||
|
||||
if (data.pure) {
|
||||
ssla_ = nullptr;
|
||||
la_ = nullptr;
|
||||
}
|
||||
} catch (nf7::Exception&) {
|
||||
ss->Finish();
|
||||
}
|
||||
void Call::Lambda::Abort() noexcept {
|
||||
if (ssla_) {
|
||||
ssla_->Abort();
|
||||
ssla_ = nullptr;
|
||||
}
|
||||
if (la_) {
|
||||
la_->Abort();
|
||||
la_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Call::UpdateItem(Sequencer::Editor&) noexcept {
|
||||
ImGui::Text("%s", mem_->callee.Stringify().c_str());
|
||||
}
|
||||
void Call::UpdateParamPanel(Sequencer::Editor&) noexcept {
|
||||
const auto em = ImGui::GetFontSize();
|
||||
|
||||
bool commit = false;
|
||||
if (ImGui::CollapsingHeader("Sequencer/Call", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
if (nf7::gui::PathButton("callee", mem_->callee, *this)) {
|
||||
commit = true;
|
||||
}
|
||||
|
||||
ImGui::InputTextMultiline("expects", &mem_->expects, {0, 4.f*em});
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||
commit = true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("session ends right after receiving these outputs");
|
||||
}
|
||||
|
||||
if (ImGui::Checkbox("pure", &mem_->pure)) {
|
||||
commit = true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("callee's lambda is created for each session");
|
||||
}
|
||||
}
|
||||
|
||||
if (commit) {
|
||||
mem_.Commit();
|
||||
}
|
||||
}
|
||||
void Call::UpdateTooltip(Sequencer::Editor&) noexcept {
|
||||
ImGui::TextUnformatted("Sequencer/Call");
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nf7
|
File diff suppressed because it is too large
Load Diff
@ -22,7 +22,6 @@
|
||||
#include "common/life.hh"
|
||||
#include "common/node.hh"
|
||||
#include "common/ptr_selector.hh"
|
||||
#include "common/sequencer.hh"
|
||||
#include "common/value.hh"
|
||||
#include "common/yas_imgui.hh"
|
||||
|
||||
@ -32,11 +31,10 @@ namespace {
|
||||
|
||||
class Curve final : public nf7::FileBase,
|
||||
public nf7::DirItem,
|
||||
public nf7::Node,
|
||||
public nf7::Sequencer {
|
||||
public nf7::Node {
|
||||
public:
|
||||
static inline const nf7::GenericTypeInfo<Curve> kType = {
|
||||
"Value/Curve", {"nf7::DirItem", "nf7::Node", "nf7::Sequencer"},
|
||||
"Value/Curve", {"nf7::DirItem", "nf7::Node"},
|
||||
"bezier curve editor",
|
||||
};
|
||||
|
||||
@ -68,8 +66,6 @@ class Curve final : public nf7::FileBase,
|
||||
nf7::FileBase(kType, env),
|
||||
nf7::DirItem(nf7::DirItem::kWidget),
|
||||
nf7::Node(nf7::Node::kCustomNode),
|
||||
nf7::Sequencer(nf7::Sequencer::kCustomItem |
|
||||
nf7::Sequencer::kParamPanel),
|
||||
life_(*this), mem_(*this, std::move(data)) {
|
||||
AssignId();
|
||||
Sanitize();
|
||||
@ -89,15 +85,11 @@ class Curve final : public nf7::FileBase,
|
||||
|
||||
std::shared_ptr<nf7::Node::Lambda> CreateLambda(
|
||||
const std::shared_ptr<nf7::Node::Lambda>&) noexcept override;
|
||||
std::shared_ptr<nf7::Sequencer::Lambda> CreateLambda(
|
||||
const std::shared_ptr<nf7::Context>&) noexcept override;
|
||||
|
||||
nf7::Node::Meta GetMeta() const noexcept override {
|
||||
return {{"x"}, {"y"}};
|
||||
}
|
||||
|
||||
void UpdateItem(nf7::Sequencer::Editor&) noexcept override;
|
||||
void UpdateParamPanel(nf7::Sequencer::Editor&) noexcept override;
|
||||
void UpdateNode(nf7::Node::Editor&) noexcept override;
|
||||
void UpdateWidget() noexcept override;
|
||||
void UpdateCurveEditorWindow(const ImVec2&) noexcept;
|
||||
@ -105,7 +97,7 @@ class Curve final : public nf7::FileBase,
|
||||
|
||||
nf7::File::Interface* interface(const std::type_info& t) noexcept override {
|
||||
return InterfaceSelector<
|
||||
nf7::DirItem, nf7::Memento, nf7::Node, nf7::Sequencer>(t).Select(this, &mem_);
|
||||
nf7::DirItem, nf7::Memento, nf7::Node>(t).Select(this, &mem_);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -318,36 +310,6 @@ std::shared_ptr<nf7::Node::Lambda> Curve::CreateLambda(
|
||||
}
|
||||
|
||||
|
||||
class Curve::SeqLambda final : public nf7::Sequencer::Lambda {
|
||||
public:
|
||||
SeqLambda(Curve& f, const std::shared_ptr<nf7::Context>& parent) noexcept :
|
||||
nf7::Sequencer::Lambda(f, parent), f_(f.life_) {
|
||||
}
|
||||
|
||||
void Run(const std::shared_ptr<nf7::Sequencer::Session>& ss) noexcept {
|
||||
try {
|
||||
ss->Send("y", nf7::Value {f_->Calc(ss->ReceiveOrThrow("x").scalar())});
|
||||
} catch (nf7::Exception&) {
|
||||
}
|
||||
ss->Finish();
|
||||
}
|
||||
|
||||
private:
|
||||
nf7::Life<Curve>::Ref f_;
|
||||
};
|
||||
std::shared_ptr<nf7::Sequencer::Lambda> Curve::CreateLambda(
|
||||
const std::shared_ptr<nf7::Context>& parent) noexcept {
|
||||
return std::make_shared<Curve::SeqLambda>(*this, parent);
|
||||
}
|
||||
|
||||
|
||||
void Curve::UpdateItem(nf7::Sequencer::Editor&) noexcept {
|
||||
ImGui::TextUnformatted("Value/Curve");
|
||||
|
||||
const auto pad = ImGui::GetStyle().WindowPadding / 2;
|
||||
ImGui::SetCursorPos(pad);
|
||||
UpdateCurveEditor(ImGui::GetContentRegionAvail()-pad);
|
||||
}
|
||||
void Curve::UpdateNode(nf7::Node::Editor&) noexcept {
|
||||
const auto em = ImGui::GetFontSize();
|
||||
ImGui::TextUnformatted("Value/Curve");
|
||||
@ -366,12 +328,6 @@ void Curve::UpdateNode(nf7::Node::Editor&) noexcept {
|
||||
ImNodes::EndSlot();
|
||||
}
|
||||
}
|
||||
void Curve::UpdateParamPanel(nf7::Sequencer::Editor&) noexcept {
|
||||
if (ImGui::CollapsingHeader("Value/Curve", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
const auto em = ImGui::GetFontSize();
|
||||
UpdateCurveEditorWindow({0, 6*em});
|
||||
}
|
||||
}
|
||||
void Curve::UpdateWidget() noexcept {
|
||||
const auto em = ImGui::GetFontSize();
|
||||
ImGui::TextUnformatted("Value/Curve");
|
||||
|
Loading…
x
Reference in New Issue
Block a user