Skip to content

Commit

Permalink
[http] Add the ability of QML devices to create / delete nodes dynami…
Browse files Browse the repository at this point in the history
…cally ; make more functions take string_view
  • Loading branch information
jcelerier committed Jun 3, 2023
1 parent c0b9134 commit e50427d
Show file tree
Hide file tree
Showing 15 changed files with 221 additions and 46 deletions.
2 changes: 1 addition & 1 deletion src/ossia-pd/src/parameter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ bool parameter::do_registration(const std::vector<t_matcher>& matchers)
name = name.substr(common_part.size());
}

auto params = ossia::net::find_or_create_parameter(*node, name, m_type->s_name);
auto params = ossia::net::find_parameter_or_create_node(*node, name, m_type->s_name);

for(auto p : params)
{
Expand Down
20 changes: 13 additions & 7 deletions src/ossia-qt/http/http_protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "http_protocol.hpp"

#include <ossia-qt/js_utilities.hpp>
#include <ossia-qt/qml_engine_functions.hpp>

#include <QJSValueIterator>
#include <QNetworkAccessManager>
Expand All @@ -23,6 +24,9 @@ http_protocol::http_protocol(QByteArray code)
, m_access{new QNetworkAccessManager}
, m_code{code}
{
auto obj = new qml_engine_functions{m_engine};
m_engine->rootContext()->setContextProperty("Device", obj);

QObject::connect(
m_access, &QNetworkAccessManager::finished, this,
[this](auto reply) {
Expand Down Expand Up @@ -95,15 +99,15 @@ bool http_protocol::push(
const ossia::net::parameter_base& parameter_base, const ossia::value& v)
{
// TODO dynamic_cast or whatever
assert(dynamic_cast<const http_parameter*>(&parameter_base));
auto& addr = static_cast<const http_parameter&>(parameter_base);

if(!addr.data().request.isEmpty())
// TODO put the http_parameters in a hash map instead?
if(auto addr = dynamic_cast<const http_parameter*>(&parameter_base))
{
sig_push(&addr, v);
return true;
if(!addr->data().request.isEmpty())
{
sig_push(addr, v);
return true;
}
}

return false;
}

Expand All @@ -120,6 +124,8 @@ bool http_protocol::observe(parameter_base&, bool enable)
void http_protocol::set_device(device_base& dev)
{
m_device = &dev;
m_device->get_capabilities();
m_engine->findChild<qml_engine_functions*>()->m_dev = &dev;
m_component->setData(m_code, QUrl{});
}

Expand Down
34 changes: 34 additions & 0 deletions src/ossia-qt/qml_engine_functions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "qml_engine_functions.hpp"

#include <wobjectimpl.h>

W_OBJECT_IMPL(ossia::net::qml_engine_functions)
namespace ossia::net
{

qml_engine_functions::qml_engine_functions(QObject* parent)
: QObject{parent}
{
}

void qml_engine_functions::addNode(QString address, QString type)
{
if(!m_dev)
return;

auto n = address.toStdString();
ossia::net::find_or_create_parameter(
m_dev->get_root_node(), address.toStdString(), type.toStdString());
}

void qml_engine_functions::removeNode(QString address, QString type)
{
if(!m_dev)
return;

if(auto res = ossia::net::find_node(m_dev->get_root_node(), address.toStdString()))
if(auto p = res->get_parent())
p->remove_child(*res);
}

}
26 changes: 26 additions & 0 deletions src/ossia-qt/qml_engine_functions.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

#include <ossia/network/base/node_functions.hpp>

#include <ossia-qt/js_utilities.hpp>

#include <QQmlContext>
#include <QQmlEngine>

namespace ossia::net
{
class qml_engine_functions : public QObject
{
W_OBJECT(qml_engine_functions)
public:
device_base* m_dev{};

explicit qml_engine_functions(QObject* parent);

void addNode(QString address, QString type);
W_SLOT(addNode)

void removeNode(QString address, QString type);
W_SLOT(removeNode)
};
}
8 changes: 4 additions & 4 deletions src/ossia/audio/audio_protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ void audio_protocol::setup_tree(int inputs, int outputs)
audio_outs.clear();

main_audio_in
= ossia::net::find_or_create_parameter<ossia::audio_parameter>(root, "/in/main");
= ossia::net::find_parameter_or_create_node<ossia::audio_parameter>(root, "/in/main");
main_audio_out
= ossia::net::find_or_create_parameter<ossia::audio_parameter>(root, "/out/main");
= ossia::net::find_parameter_or_create_node<ossia::audio_parameter>(root, "/out/main");
for(int i = 0; i < inputs; i++)
{
audio_ins.push_back(ossia::net::find_or_create_parameter<ossia::audio_parameter>(
audio_ins.push_back(ossia::net::find_parameter_or_create_node<ossia::audio_parameter>(
root, "/in/" + std::to_string(i + 1)));
}
for(int i = 0; i < outputs; i++)
{
audio_outs.push_back(ossia::net::find_or_create_parameter<ossia::audio_parameter>(
audio_outs.push_back(ossia::net::find_parameter_or_create_node<ossia::audio_parameter>(
root, "/out/" + std::to_string(i + 1)));
}

Expand Down
96 changes: 96 additions & 0 deletions src/ossia/detail/case_insensitive.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#pragma once
#include <ossia/detail/config.hpp>

#include <ossia/detail/algorithms.hpp>
#include <ossia/detail/hash.hpp>
#include <ossia/detail/hash_map.hpp>

namespace ossia
{
static constexpr unsigned char ascii_tolower(unsigned char c) noexcept
{
constexpr unsigned char A = 'A', Z = 'Z', a = 'a';
if(c >= A && c <= Z)
return c - (A - a);
else
return c;
}

struct ascii_tolower_less
{
constexpr bool operator()(unsigned char c1, unsigned char c2) const noexcept
{
return ascii_tolower(c1) < ascii_tolower(c2);
}
};

struct ascii_tolower_equal
{
constexpr bool operator()(unsigned char c1, unsigned char c2) const noexcept
{
return ascii_tolower(c1) == ascii_tolower(c2);
}
};

struct case_insensitive_comparator
{
using is_transparent = std::true_type;
bool operator()(std::string_view s1, std::string_view s2) const noexcept
{
return std::lexicographical_compare(
s1.begin(), s1.end(), s2.begin(), s2.end(), ascii_tolower_less{});
}
};

struct case_insensitive_hash
{
static constexpr int buffer_size = 64;
using is_transparent = std::true_type;
using is_avalanching = std::true_type;

uint64_t operator()(std::string_view s1) const noexcept
{
namespace wyhash = ankerl::unordered_dense::detail::wyhash;

uint64_t res = UINT64_C(0xff51afd7ed558ccd);
unsigned char buf[buffer_size + 1];
const char* ptr = s1.data();
uint64_t remaining = s1.size();

while(remaining > 0)
{
if(remaining >= buffer_size)
{
for(int i = 0; i < buffer_size; i++)
buf[i] = ascii_tolower(ptr[i]);
res = wyhash::mix(res, wyhash::hash(buf, buffer_size));
remaining -= buffer_size;
ptr += buffer_size;
}
else
{
[[likely]];
for(uint64_t i = 0; i < remaining; i++)
buf[i] = ascii_tolower(ptr[i]);
res = wyhash::mix(res, wyhash::hash(buf, remaining));
remaining = 0;
}
}

return res;
}
};

struct case_insensitive_equal
{
using is_transparent = std::true_type;
constexpr bool operator()(std::string_view s1, std::string_view s2) const noexcept
{
return std::equal(s1.begin(), s1.end(), s2.begin(), s2.end(), ascii_tolower_equal{});
}
};

template <typename Value>
using case_insensitive_string_map
= ossia::hash_map<std::string, Value, case_insensitive_hash, case_insensitive_equal>;
}
28 changes: 21 additions & 7 deletions src/ossia/network/base/node_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -957,35 +957,49 @@ void fuzzysearch(
#endif
}

std::pair<std::vector<std::string>, bool> expand_address(const std::string& address)
std::pair<std::vector<std::string>, bool> expand_address(std::string address)
{
std::vector<std::string> names;
bool pattern_matching = is_brace_expansion(address);

if(pattern_matching)
{
// 1. Replace all [ ] with { } form
auto str = canonicalize_str(address);
auto str = canonicalize_str(std::move(address));

// 2. Expand
names = expand(str);
}
else
{
names.push_back(address);
names.push_back(std::move(address));
}

return {names, pattern_matching};
return {std::move(names), pattern_matching};
}

std::vector<parameter_base*> find_or_create_parameter(
node_base& node, const std::string& address, const std::string& type)
parameter_base* find_or_create_parameter(
node_base& node, std::string_view address, std::string_view type)
{
auto& n = ossia::net::find_or_create_node(node, address);
{
if(auto param = n.get_parameter())
return param;
else if(auto param = ossia::try_setup_parameter(type, n))
return param;
else
return nullptr;
}
}

std::vector<parameter_base*> find_parameter_or_create_node(
node_base& node, std::string_view address, std::string_view type)
{
// search for child that matches name but without parameter
// and create parameter on that node if it exists
// or create a new node with that name and a parameter

auto [names, pattern_matching] = expand_address(address);
auto [names, pattern_matching] = expand_address(std::string(address));

std::vector<node_base*> nodes{};
nodes.reserve(names.size());
Expand Down
10 changes: 6 additions & 4 deletions src/ossia/network/base/node_functions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ create_nodes(node_base& dev, std::string_view pattern);
OSSIA_EXPORT node_base&
find_or_create_node(node_base& dev, std::string_view parameter_base);

parameter_base* find_or_create_parameter(
node_base& node, std::string_view address, std::string_view type);
/**
* @brief Find a parameter and create it if it does not exist.
* @details Find a node matching the address, if it already has a parameter
Expand All @@ -74,8 +76,8 @@ find_or_create_node(node_base& dev, std::string_view parameter_base);
* @param type of the parameter
* @return vector of created parameters
*/
OSSIA_EXPORT std::vector<parameter_base*> find_or_create_parameter(
node_base& node, const std::string& address, const std::string& type);
OSSIA_EXPORT std::vector<parameter_base*> find_parameter_or_create_node(
node_base& node, std::string_view address, std::string_view type);

/**
* @brief Calls find_node or create_node according to the value `create`
Expand Down Expand Up @@ -107,7 +109,7 @@ auto create_parameter(ossia::net::node_base& root, std::string name)
}

template <typename Address>
auto find_or_create_parameter(ossia::net::node_base& root, std::string_view name)
auto find_parameter_or_create_node(ossia::net::node_base& root, std::string_view name)
{
auto& node = ossia::net::find_or_create_node(root, std::move(name));
if(auto p = dynamic_cast<Address*>(node.get_parameter()))
Expand All @@ -127,7 +129,7 @@ OSSIA_EXPORT
void expand_ranges(std::string& str);

OSSIA_EXPORT
std::pair<std::vector<std::string>, bool> expand_address(const std::string& address);
std::pair<std::vector<std::string>, bool> expand_address(std::string address);

/**
* @brief list_all_children : list all child nodes recursively
Expand Down
15 changes: 7 additions & 8 deletions src/ossia/network/common/complex_type.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <ossia/detail/apply.hpp>
#include <ossia/detail/case_insensitive.hpp>
#include <ossia/network/base/node.hpp>
#include <ossia/network/base/node_attributes.hpp>
#include <ossia/network/base/parameter.hpp>
Expand Down Expand Up @@ -105,10 +106,11 @@ net::parameter_base* setup_parameter(const complex_type& t, net::node_base& node
return ossia::apply(setup_parameter_visitor{node}, t);
}

static const ossia::string_map<net::parameter_data>& parameter_creation_map()
static const ossia::case_insensitive_string_map<net::parameter_data>&
parameter_creation_map()
{
static const auto map = [] {
ossia::string_map<net::parameter_data> t;
ossia::case_insensitive_string_map<net::parameter_data> t;
ossia::detail::list_units([&](std::string e, ossia::unit_t u) {
net::parameter_data p;
p.type = u;
Expand Down Expand Up @@ -246,10 +248,9 @@ const ossia::net::parameter_data* default_parameter_for_type(std::string_view ty
return it != map.end() ? &(it->second) : nullptr;
}

net::parameter_base* try_setup_parameter(std::string str, net::node_base& node)
net::parameter_base* try_setup_parameter(std::string_view str, net::node_base& node)
{
auto& map = parameter_creation_map();
boost::algorithm::to_lower(str);
auto it = map.find(str);
if(it != map.end())
{
Expand All @@ -259,15 +260,13 @@ net::parameter_base* try_setup_parameter(std::string str, net::node_base& node)
}

net::parameter_base*
create_parameter(net::node_base& parent, std::string node, std::string str)
create_parameter(net::node_base& parent, std::string_view node, std::string_view str)
{
auto& map = parameter_creation_map();
boost::algorithm::to_lower(str);
auto it = map.find(str);
if(it != map.end())
{
return setup_parameter(
it->second.type, ossia::net::create_node(parent, std::move(node)));
return setup_parameter(it->second.type, ossia::net::create_node(parent, node));
}
return nullptr;
}
Expand Down
Loading

0 comments on commit e50427d

Please sign in to comment.