Compare commits

...

2 Commits

18 changed files with 731 additions and 3556 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -14,361 +14,127 @@
#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")) {
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) {
const auto& v = CheckRef<nf7::Value>(L, 1, "nf7::Value");
lua_pushstring(L, v.typeName());
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, "type");
lua_setfield(L, -2, "now");
// ---- value lib ----
// value(entity) -> value
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));
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 {
lua_setfield(L, -2, p.first.c_str());
return luaL_error(L, "unknown type specifier: %s", type);
}
} else {
Push(L, Check<nf7::Value>(L, 1));
}
}
};
std::visit(Visitor {.L = L}, v.value());
return 1;
});
lua_setfield(L, -2, "value");
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, [](auto L) {
CheckRef<nf7::Value>(L, 1, "nf7::Value").~Value();
return 0;
});
lua_setfield(L, -2, "__gc");
}
//// 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);
}
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, 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);
if (lua_isnoneornil(L, idx)) {
return nf7::Value {nf7::Value::Pulse {}};
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);
}
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");
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;
} else {
return luaL_error(L, "#2 argument must be vector or mutable vector");
}
const auto src_off = luaL_checkinteger(L, 4);
const lua_Integer size = luaL_checkinteger(L, 5);
if (size < 0) {
return luaL_error(L, "negative size");
}
if (dst_off < 0 || static_cast<size_t>(dst_off+size) > dst.size()) {
return luaL_error(L, "dst out of bounds");
}
if (src_off < 0 || static_cast<size_t>(src_off+size) > src->size()) {
return luaL_error(L, "src out of bounds");
}
std::memcpy(dst. data()+static_cast<size_t>(dst_off),
src->data()+static_cast<size_t>(src_off),
static_cast<size_t>(size));
return 0;
});
lua_setfield(L, -2, "blit");
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, [](auto L) {
CheckRef<T>(L, 1, kTypeName).~vector();
return 0;
});
lua_setfield(L, -2, "__gc");
}
lua_setmetatable(L, -2);
}
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, "nf7::NodeRootLambda")) {
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 = CheckNodeRootLambda(L, 1);
la->ExecSend(luaL_checkstring(L, 2), luajit::CheckValue(L, 3));
auto la = Check<T>(L, 1);
la->ExecSend(luaL_checkstring(L, 2), Check<nf7::Value>(L, 3));
return 0;
});
lua_setfield(L, -2, "send");
// la:recv(nf7, {name1, name2, ...})
lua_pushcfunction(L, [](auto L) {
auto la = CheckNodeRootLambda(L, 1);
auto la = Check<T>(L, 1);
auto th = luajit::Thread::GetPtr(L, 2);
std::vector<std::string> names;
ToStringList(L, 3, names);
const auto names = Check<std::vector<std::string>>(L, 3);
if (names.size() == 0) {
return 0;
}
@ -379,7 +145,7 @@ void PushNodeRootLambda(
try {
const auto& p = fu.value();
lua_pushstring(L, p.first.c_str());
luajit::PushValue(L, p.second);
luajit::Push(L, p.second);
return 2;
} catch (nf7::Exception&) {
return 0;
@ -398,26 +164,23 @@ void PushNodeRootLambda(
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, [](auto L) {
CheckNodeRootLambda(L, 1).~shared_ptr();
Check<T>(L, 1).~shared_ptr();
return 0;
});
lua_setfield(L, -2, "__gc");
}
lua_setmetatable(L, -2);
}
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

View File

@ -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) {
}
template <std::floating_point T>
void Push(lua_State* L, T v) noexcept {
lua_pushnumber(L, static_cast<lua_Number>(v));
} else if constexpr (std::is_null_pointer<T>::value) {
lua_pushnil(L);
} else {
[] <bool F = false>() { static_assert(F, "T is invalid"); }();
}
}
inline void Push(lua_State* L, const std::string& v) noexcept {
lua_pushstring(L, v.c_str());
}
inline void Push(lua_State* L, const Value& v) noexcept {
luajit::PushValue(L, v);
}
inline void Push(lua_State* L, const nf7::Value::Vector& v) noexcept {
luajit::PushVector(L, v);
}
inline void Push(lua_State* L, const std::vector<uint8_t>& v) noexcept {
luajit::PushMutableVector(L, std::vector<uint8_t> {v});
}
inline void Push(lua_State* L, std::vector<uint8_t>&& v) noexcept {
luajit::PushMutableVector(L, std::move(v));
}
inline void Push(lua_State* L, const std::shared_ptr<nf7::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

View File

@ -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,

View File

@ -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

View File

@ -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);

View File

@ -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
View 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

View File

@ -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

View File

@ -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,92 +27,117 @@ 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>;
class Pulse { };
using Boolean = bool;
using Integer = int64_t;
using Scalar = double;
using String = std::string;
using Vector = std::shared_ptr<std::vector<uint8_t>>;
using Tuple = std::shared_ptr<std::vector<TuplePair>>;
using DataPtr = std::shared_ptr<Data>;
using ConstVector = std::shared_ptr<const std::vector<uint8_t>>;
using ConstTuple = std::shared_ptr<const std::vector<TuplePair>>;
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;
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>(); }
const Buffer& vector() const { return get<Buffer>(); }
const Tuple& tuple() const { return get<Tuple>(); }
const auto& value() const noexcept { return value_; }
// direct reference accessor
@ -120,52 +145,38 @@ class Value {
Boolean& boolean() { return get<Boolean>(); }
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,14 +184,14 @@ 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"};
}
}
@ -192,21 +203,19 @@ class Value {
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()(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");
}
};

View File

@ -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;

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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");