diff --git a/clap/CMakeLists.txt b/clap/CMakeLists.txt deleted file mode 100644 index b7b73fe..0000000 --- a/clap/CMakeLists.txt +++ /dev/null @@ -1,95 +0,0 @@ -cmake_minimum_required(VERSION 3.28) - -set(CMAKE_CXX_VISIBILITY_PRESET hidden) -set(CMAKE_C_VISIBILITY_PRESET hidden) -set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) - -project(plugins VERSION 1.0.0) - -################ boilerplate config - -set(CMAKE_CXX_STANDARD 17) # for string_view - -if (APPLE) - set(CMAKE_OSX_DEPLOYMENT_TARGET 10.13) - set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64") - enable_language(OBJCXX) -endif () - -include(FetchContent) -FetchContent_Declare( - clap - GIT_REPOSITORY https://github.com/geraintluff/clap.git # https://github.com/free-audio/clap - GIT_TAG 2df92fe17911f15436176b9c37faec370166d14f # draft/web - GIT_SHALLOW OFF -) -FetchContent_MakeAvailable(clap) - -################ CLAP wrapper stuff - -if(NOT DEFINED VST3_SDK_ROOT) - if(EMSCRIPTEN) - # don't download the VST3 SDK - set(VST3_SDK_ROOT "./dummy/vst3sdk/path") - else() - set(CLAP_WRAPPER_DOWNLOAD_DEPENDENCIES TRUE) - endif() -endif() - -set(CLAP_WRAPPER_OUTPUT_NAME clap-wrapper-target) -set(CLAP_WRAPPER_DONT_ADD_TARGETS TRUE) - -include(FetchContent) -FetchContent_Declare( - clap-wrapper - GIT_REPOSITORY https://github.com/geraintluff/clap-wrapper.git - GIT_TAG cd666f7d2291d47f810d9c8f123886026a631576 - GIT_SHALLOW ON -) -FetchContent_MakeAvailable(clap-wrapper) - -################ Helpers - -FetchContent_Declare( - clap-helpers - GIT_REPOSITORY https://github.com/free-audio/clap-helpers - GIT_TAG 58ab81b1dc8219e859529c1306f364bb3aedf7d5 - GIT_SHALLOW OFF -) -FetchContent_MakeAvailable(clap-helpers) - -################ The actual plugin(s) - -add_subdirectory(../ signalsmith-basics/) # need explicit path since it's not a subdir - -set(NAME basics) -add_library(${NAME}_static STATIC) -target_link_libraries(${NAME}_static PUBLIC - clap - signalsmith-basics -) -target_sources(${NAME}_static PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/source/basics.cpp -) - -make_clapfirst_plugins( - TARGET_NAME ${NAME} - IMPL_TARGET ${NAME}_static - - RESOURCE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/resources" - - OUTPUT_NAME "${NAME}" - ENTRY_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/source/clap_entry.cpp - - BUNDLE_IDENTIFIER "uk.co.signalsmith-audio.plugins.${NAME}" - BUNDLE_VERSION "1.0.0" - WINDOWS_FOLDER_VST3 TRUE - - PLUGIN_FORMATS CLAP VST3 WCLAP # AUV2 - COPY_AFTER_BUILD FALSE - - AUV2_MANUFACTURER_NAME "Signalsmith Audio" - AUV2_MANUFACTURER_CODE "SigA" - AUV2_SUBTYPE_CODE "BdDt" - AUV2_INSTRUMENT_TYPE "aufx" -) diff --git a/clap/Makefile b/clap/Makefile deleted file mode 100644 index 7259d1a..0000000 --- a/clap/Makefile +++ /dev/null @@ -1,52 +0,0 @@ -.PHONY: build build-emscripten emsdk -PROJECT := plugins -PLUGIN := basics - -CMAKE_PARAMS := -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=.. -G Xcode # -DCMAKE_BUILD_TYPE=Release - -mac-installer: mac-installer-$(PLUGIN) - -clean: - rm -rf out - -build: - cmake . -B out/build $(CMAKE_PARAMS) - -release-%: build - cmake --build out/build --target $* --config Release - -mac-installer-%: release-%_vst3 release-%_clap emscripten-%_wclap - ../stfx/front-end/clap/mac/make-pkg-installer.sh out/$*.pkg $* uk.co.signalsmith.installer.stfx.$* out/Release/$* - -####### Emscripten ####### -# based on https://stunlock.gg/posts/emscripten_with_cmake/ - -CURRENT_DIR := $(shell pwd) -EMSDK ?= $(CURRENT_DIR)/emsdk -EMSDK_ENV = unset CMAKE_TOOLCHAIN_FILE; EMSDK_QUIET=1 . "$(EMSDK)/emsdk_env.sh"; - -emsdk: - @ if ! test -d "$(EMSDK)" ;\ - then \ - echo "SDK not found - cloning from Github" ;\ - git clone https://github.com/emscripten-core/emsdk.git "$(EMSDK)" ;\ - cd "$(EMSDK)" && git pull && ./emsdk install latest && ./emsdk activate latest ;\ - $(EMSDK_ENV) emcc --check && python3 --version && cmake --version ;\ - fi - -build-emscripten: emsdk - $(EMSDK_ENV) emcmake cmake . -B out/build-emscripten -DCMAKE_RUNTIME_OUTPUT_DIRECTORY=../Release -DCMAKE_BUILD_TYPE=Release - -emscripten-%: build-emscripten - $(EMSDK_ENV) cmake --build out/build-emscripten --target $* --config Release - -####### Dev stuff ####### - -format: - find source -iname \*.cpp -o -iname \*.h | xargs clang-format -i - -dev: release-$(PLUGIN)_clap - /Applications/REAPER.app/Contents/MacOS/REAPER REAPER/$(PLUGIN)/$(PLUGIN).RPP - -wclap: emscripten-$(PLUGIN)_wclap - ./wclap-tar.sh out/Release/$(PLUGIN).wclap out/ \ No newline at end of file diff --git a/clap/REAPER/basics/basics.RPP b/clap/REAPER/basics/basics.RPP deleted file mode 100644 index 03033a6..0000000 --- a/clap/REAPER/basics/basics.RPP +++ /dev/null @@ -1,159 +0,0 @@ - - RIPPLE 0 - GROUPOVERRIDE 0 0 0 - AUTOXFADE 129 - ENVATTACH 3 - POOLEDENVATTACH 0 - MIXERUIFLAGS 11 48 - ENVFADESZ10 40 - PEAKGAIN 1 - FEEDBACK 0 - PANLAW 1 - PROJOFFS 0 0 0 - MAXPROJLEN 0 0 - GRID 3199 8 1 8 1 0 0 0 - TIMEMODE 1 5 -1 30 0 0 -1 - VIDEO_CONFIG 0 0 256 - PANMODE 3 - PANLAWFLAGS 3 - CURSOR 0 - ZOOM 100 0 0 - VZOOMEX 6 0 - USE_REC_CFG 0 - RECMODE 1 - SMPTESYNC 0 30 100 40 1000 300 0 0 1 0 0 - LOOP 0 - LOOPGRAN 0 4 - RECORD_PATH "Media" "" - - - RENDER_FILE "" - RENDER_PATTERN "" - RENDER_FMT 0 2 0 - RENDER_1X 0 - RENDER_RANGE 1 0 0 18 1000 - RENDER_RESAMPLE 3 0 1 - RENDER_ADDTOPROJ 0 - RENDER_STEMS 0 - RENDER_DITHER 0 - TIMELOCKMODE 1 - TEMPOENVLOCKMODE 1 - ITEMMIX 1 - DEFPITCHMODE 589824 0 - TAKELANE 1 - SAMPLERATE 44100 0 0 - - LOCK 1 - - GLOBAL_AUTO -1 - TEMPO 120 4 4 - PLAYRATE 1 0 0.25 4 - SELECTION 0 0 - SELECTION2 0 0 - MASTERAUTOMODE 0 - MASTERTRACKHEIGHT 0 0 - MASTERPEAKCOL 16576 - MASTERMUTESOLO 0 - MASTERTRACKVIEW 1 0.6667 0.5 0.5 0 0 0 0 0 0 0 0 0 0 - MASTERHWOUT 0 0 1 0 0 0 0 -1 - MASTER_NCH 4 2 - MASTER_VOLUME 1 0 -1 -1 1 - MASTER_PANMODE 3 - MASTER_PANLAWFLAGS 3 - MASTER_FX 1 - MASTER_SEL 0 - - FLOATPOS 0 0 0 0 - FXID {81F27681-ACFE-1D40-9AAE-F8C9A0035B1F} - WAK 0 0 - BYPASS 0 0 0 - "" - cnRzcu5e7f4IAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAAgAAAABAAAAAAAAAAIAAAAAAAAA - BAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAABAAAAAAAAAAIAAAAAAAAAArAAAAAEAAAAAABAA - AQAAAAIAAAABAAAAZGVmYXVsdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxMjcuMC4wLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== - AFByb2dyYW0gMQAQAAAA - > - FLOATPOS 0 0 0 0 - FXID {11A0D811-6420-CB40-95D6-F9D12E45C862} - WAK 0 0 - > - - - - - > -> diff --git a/clap/env.sh.html b/clap/env.sh.html deleted file mode 100644 index 373aff3..0000000 --- a/clap/env.sh.html +++ /dev/null @@ -1,7 +0,0 @@ - - -
\ No newline at end of file diff --git a/clap/source/basics.cpp b/clap/source/basics.cpp deleted file mode 100644 index 9d2f918..0000000 --- a/clap/source/basics.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#ifndef LOG_EXPR -# include -# define LOG_EXPR(expr) std::cout << #expr " = " << (expr) << std::endl; -#endif - -#include "signalsmith-basics/analyser.h" -#include "signalsmith-basics/chorus.h" -#include "signalsmith-basics/crunch.h" -#include "signalsmith-basics/freq-shifter.h" -#include "signalsmith-basics/limiter.h" -#include "signalsmith-basics/reverb.h" - -#include "../../stfx/clap/stfx-clap.h" - -template -struct AnalyserSTFX : public signalsmith::basics::AnalyserSTFX { - AnalyserSTFX() { - this->webPage += "?compact&columns"; - } -}; - -static stfx::clap::Plugins plugins; -bool clap_init(const char *path) { - plugins.add({ - .clap_version = CLAP_VERSION, - .id = "uk.co.signalsmith.basics.analyser", - .name = "[Basics] Analyser", - .vendor = "Signalsmith Audio", - .url = "", - .manual_url = "", - .support_url = "", - .version = "1.0.0" - }, { - CLAP_PLUGIN_FEATURE_ANALYZER, - }); - - plugins.add({ - .clap_version = CLAP_VERSION, - .id = "uk.co.signalsmith.basics.chorus", - .name = "[Basics] Chorus", - .vendor = "Signalsmith Audio", - .url = "", - .manual_url = "", - .support_url = "", - .version = "1.0.0" - }, { - CLAP_PLUGIN_FEATURE_AUDIO_EFFECT, - CLAP_PLUGIN_FEATURE_CHORUS, - }); - - plugins.add({ - .clap_version = CLAP_VERSION, - .id = "uk.co.signalsmith.basics.crunch", - .name = "[Basics] Crunch", - .vendor = "Signalsmith Audio", - .url = "", - .manual_url = "", - .support_url = "", - .version = "1.0.0" - }, { - CLAP_PLUGIN_FEATURE_AUDIO_EFFECT, - CLAP_PLUGIN_FEATURE_DISTORTION, - }); - - plugins.add({ - .clap_version = CLAP_VERSION, - .id = "uk.co.signalsmith.basics.freq-shifter", - .name = "[Basics] Frequency Shifter", - .vendor = "Signalsmith Audio", - .url = "", - .manual_url = "", - .support_url = "", - .version = "1.0.0" - }, { - CLAP_PLUGIN_FEATURE_AUDIO_EFFECT, - CLAP_PLUGIN_FEATURE_FREQUENCY_SHIFTER, - }); - - plugins.add({ - .clap_version = CLAP_VERSION, - .id = "uk.co.signalsmith.basics.limiter", - .name = "[Basics] Limiter", - .vendor = "Signalsmith Audio", - .url = "", - .manual_url = "", - .support_url = "", - .version = "1.0.0" - }, { - CLAP_PLUGIN_FEATURE_AUDIO_EFFECT, - CLAP_PLUGIN_FEATURE_LIMITER, - }); - - plugins.add({ - .clap_version = CLAP_VERSION, - .id = "uk.co.signalsmith.basics.reverb", - .name = "[Basics] Reverb", - .vendor = "Signalsmith Audio", - .url = "", - .manual_url = "", - .support_url = "", - .version = "1.0.0" - }, { - CLAP_PLUGIN_FEATURE_AUDIO_EFFECT, - CLAP_PLUGIN_FEATURE_REVERB, - }); - - return plugins.clap_init(path); -} -void clap_deinit() { - plugins.clap_deinit(); -} -const void * clap_get_factory(const char *id) { - return plugins.clap_get_factory(id); -} diff --git a/clap/source/clap_entry.cpp b/clap/source/clap_entry.cpp deleted file mode 100644 index c025876..0000000 --- a/clap/source/clap_entry.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "clap/entry.h" -/* -#include "../../stfx/clap/stfx-clap.h" - -stfx::clap::Plugins stfxPlugins; -extern void addAnalyser(); -extern void addCrunch(); -extern void addLimiter(); -extern void addReverb(); - -bool clap_init(const char *path) { - static bool added = false; - if (!added) { - addAnalyser(); - addCrunch(); - addLimiter(); - addReverb(); - } - return added = stfxPlugins.clap_init(path); -} -void clap_deinit() { - stfxPlugins.clap_deinit(); -} -const void * clap_get_factory(const char *id) { - return stfxPlugins.clap_get_factory(id); -} -*/ - -extern bool clap_init(const char *path); -extern void clap_deinit(); -extern const void * clap_get_factory(const char *id); - -extern "C" { - const CLAP_EXPORT clap_plugin_entry clap_entry{ - .clap_version = CLAP_VERSION, - .init = clap_init, - .deinit = clap_deinit, - .get_factory = clap_get_factory - }; -} diff --git a/clap/wclap-tar.sh b/clap/wclap-tar.sh deleted file mode 100755 index 09ecf3a..0000000 --- a/clap/wclap-tar.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env sh - -name="$(basename $1)" - -outDir="${2:-..}" -pushd "${outDir}" -outDir=`pwd` -popd - -echo $name - -cd $1 -rm -f "${outDir}/${name}.tar.gz" -tar --exclude=".*" -vczf "${outDir}/${name}.tar.gz" * diff --git a/stfx/clap/stfx-clap.h b/stfx/clap/stfx-clap.h deleted file mode 100644 index 59bed02..0000000 --- a/stfx/clap/stfx-clap.h +++ /dev/null @@ -1,656 +0,0 @@ -#include "clap/clap.h" - -#include -#include -#include -#include -#include -#include - -#include "../param-info.h" -#include "../storage/stfx-storage.h" -#include "../ui/web-ui.h" - -namespace stfx { namespace clap { - -// A CLAP plugin made from an STFX template -template class EffectSTFX> -struct Plugin; - -// A helper to make a CLAP plugin factory from STFX templates -struct Plugins { - template class EffectSTFX, class ...Args> - size_t add(clap_plugin_descriptor desc, std::initializer_list features, Args ...args) { - size_t index = featureLists.size(); - - featureLists.emplace_back(features); - featureLists[index].push_back(nullptr); - desc.features = featureLists[index].data(); - descriptors.push_back(desc); - - creates.push_back([=](const clap_host *host){ - return new Plugin(*this, &descriptors[index], host, args...); - }); - return index; - } - - bool clap_init(const char *path) { - modulePath = path; - return true; - } - - void clap_deinit() {} - - const void * clap_get_factory(const char *id) { - if (!std::strcmp(id, CLAP_PLUGIN_FACTORY_ID)) { - // static variables like this are thread-safe (since C++11) - // https://en.cppreference.com/w/cpp/language/storage_duration.html#Static_block_variables - static PluginFactory factory{*this}; - return &factory; - } - return nullptr; - } - - std::string modulePath; -private: - std::vector> featureLists; - std::vector descriptors; - std::vector> creates; - - struct PluginFactory : public clap_plugin_factory { - Plugins &plugins; - - PluginFactory(Plugins &plugins) : plugins(plugins) { - get_plugin_count = static_get_plugin_count; - get_plugin_descriptor = static_get_plugin_descriptor; - create_plugin = static_create_plugin; - } - - static uint32_t static_get_plugin_count(const clap_plugin_factory *factory) { - const auto &plugins = ((PluginFactory *)factory)->plugins; - return uint32_t(plugins.creates.size()); - } - - static const clap_plugin_descriptor * static_get_plugin_descriptor(const clap_plugin_factory *factory, uint32_t index) { - const auto &plugins = ((PluginFactory *)factory)->plugins; - if (index >= plugins.descriptors.size()) return nullptr; - return &plugins.descriptors[index]; - } - - static const clap_plugin * static_create_plugin(const clap_plugin_factory *factory, const clap_host *host, const char *pluginId) { - const auto &plugins = ((PluginFactory *)factory)->plugins; - for (size_t index = 0; index < plugins.descriptors.size(); ++index) { - auto &desc = plugins.descriptors[index]; - if (!std::strcmp(pluginId, desc.id)) { - return plugins.creates[index](host); - } - } - return nullptr; - } - }; -}; - -struct Crc32 { - void add(uint8_t byte) { - uint32_t val = (crc^byte)&0xFF; - for (int i = 0; i < 8; ++i) { - val = (val&1) ? (val>>1)^0xEDB88320 : (val>>1); - } - crc = val^(crc>>8); - } - - Crc32 & addString(const char *str, bool includeNull=true) { - while (*str) { - add(uint8_t(*str)); - ++str; - } - if (includeNull) add(0); - return *this; - } - - Crc32 copy() { - return {*this}; - } - - uint32_t done() const { - return crc^0xFFFFFFFFu; - } -private: - uint32_t crc = 0xFFFFFFFFu; -}; - -// Just include the proposed draft structs here -static constexpr const char *CLAP_EXT_WEBVIEW1 = "clap.webview/1"; -struct clap_plugin_webview1 { - bool(CLAP_ABI *provide_starting_uri)(const clap_plugin_t *plugin, char *out_buffer, uint32_t out_buffer_capacity); - bool(CLAP_ABI *receive)(const clap_plugin_t *plugin, const void *buffer, uint32_t size); -}; -struct clap_host_webview1 { - bool(CLAP_ABI *is_open)(const clap_host_t *host); - bool(CLAP_ABI *send)(const clap_host_t *host, const void *buffer, uint32_t size); -}; - -template class EffectSTFX> -struct Plugin : public clap_plugin { - const Plugins &plugins; - const clap_host *host; - const clap_host_params *hostParams = nullptr; - const clap_host_webview1 *hostWebview1 = nullptr; - using Effect = stfx::web::WebUILibraryEffect; - Effect effect; - - template - Plugin(const Plugins &plugins, const clap_plugin_descriptor *desc, const clap_host *host, Args ...args) : plugins(plugins), host(host), effect(args...) { - this->desc = desc; - this->plugin_data = nullptr; - this->init = plugin_init; - this->destroy = plugin_destroy; - this->activate = plugin_activate; - this->deactivate = plugin_deactivate; - this->start_processing = plugin_start_processing; - this->stop_processing = plugin_stop_processing; - this->reset = plugin_reset; - this->process = plugin_process; - this->get_extension = plugin_get_extension; - this->on_main_thread = plugin_on_main_thread; - - scanParams(); - } - - // plugin state - bool isConfigured = false; - // for library STFX, all inputs/outputs (main and aux) get concatenated together - std::vector inputBuffers; - std::vector outputBuffers; - - void processEvent(const clap_event_header *header) { - if (header->space_id != CLAP_CORE_EVENT_SPACE_ID) return; // only core events supported atm - if (header->type == CLAP_EVENT_PARAM_VALUE) { - auto *event = (clap_event_param_value *)header; - auto *param = (Param *)event->cookie; - if (!param) { - auto paramId = event->param_id; - for (auto &p : params) { - if (p.id == paramId) { - param = &p; - break; - } - } - if (!param) return; // invalid parameter - } else if (param->id != event->param_id) { - return; // inconsistent ID / cookie - } - if (param->rangeParam) { - *param->rangeParam = param->rangeInfo->fromUnit(event->value); - } else { - *param->steppedParam = int(std::round(event->value)); - } - } else { - LOG_EXPR(header->size); - LOG_EXPR(header->time); - LOG_EXPR(header->space_id); - LOG_EXPR(header->type); - LOG_EXPR(header->flags); - } - } - - // CLAP plugin methods - static bool plugin_init(const clap_plugin *obj) { - auto &plugin = *(Plugin *)obj; -#define STFX_GET_EXT(field, extId) \ - plugin.field = (decltype(plugin.field))plugin.host->get_extension(plugin.host, extId); - STFX_GET_EXT(hostParams, CLAP_EXT_PARAMS) - STFX_GET_EXT(hostWebview1, CLAP_EXT_WEBVIEW1) -#undef STFX_GET_EXT - return true; - } - static void plugin_destroy(const clap_plugin *obj) { - delete (Plugin *)obj; - } - static bool plugin_activate(const clap_plugin *obj, double sampleRate, uint32_t minFrames, uint32_t maxFrames) { - auto &plugin = *(Plugin *)obj; - auto &config = plugin.effect.config; - if (!plugin.isConfigured || sampleRate != plugin.effect.config.sampleRate) { - auto prevConfig = config; - plugin.isConfigured = plugin.effect.configure(); - if (!plugin.isConfigured) { - // Can't change config (e.g. sample-rate/ports/etc.) here, return to previous - config = prevConfig; - } - } - size_t inputChannels = config.inputChannels; - for (auto &a : config.auxInputs) inputChannels += a; - plugin.inputBuffers.reserve(inputChannels); - size_t outputChannels = config.outputChannels; - for (auto &a : config.auxOutputs) a += outputChannels; - plugin.outputBuffers.reserve(outputChannels); - return plugin.isConfigured; - } - static void plugin_deactivate(const clap_plugin *obj) {} - static bool plugin_start_processing(const clap_plugin *obj) { - auto &plugin = *(Plugin *)obj; - return plugin.isConfigured; - } - static void plugin_stop_processing(const clap_plugin *obj) {} - static void plugin_reset(const clap_plugin *obj) { - auto &plugin = *(Plugin *)obj; - if (plugin.isConfigured) plugin.effect.reset(); - } - static const void * plugin_get_extension(const clap_plugin *obj, const char *extId) { - if (!std::strcmp(extId, CLAP_EXT_PARAMS)) { - static struct clap_plugin_params ext{ - params_count, - params_get_info, - params_get_value, - params_value_to_text, - params_text_to_value, - params_flush - }; - return &ext; - } else if (!std::strcmp(extId, CLAP_EXT_AUDIO_PORTS)) { - static struct clap_plugin_audio_ports ext{ - audio_ports_count, - audio_ports_get - }; - return &ext; - } else if (!std::strcmp(extId, CLAP_EXT_STATE)) { - static struct clap_plugin_state ext{ - state_save, - state_load - }; - return &ext; - } else if (!std::strcmp(extId, CLAP_EXT_WEBVIEW1)) { - static struct clap_plugin_webview1 ext{ - webview1_provide_starting_uri, - webview1_receive - }; - return &ext; - } - return nullptr; - } - static clap_process_status plugin_process(const clap_plugin *obj, const clap_process *process) { - auto &plugin = *(Plugin *)obj; - auto &inputBuffers = plugin.inputBuffers; - for (uint32_t i = 0; i < process->audio_inputs_count; ++i) { - auto &buffer = process->audio_inputs[i]; - for (uint32_t c = 0; c < buffer.channel_count; ++c) { - inputBuffers.push_back(buffer.data32[c]); - } - } - auto &outputBuffers = plugin.outputBuffers; - for (uint32_t i = 0; i < process->audio_outputs_count; ++i) { - auto &buffer = process->audio_outputs[i]; - for (uint32_t c = 0; c < buffer.channel_count; ++c) { - outputBuffers.push_back(buffer.data32[c]); - } - } - - size_t length = process->frames_count; - auto inputEventCount = process->in_events->size(process->in_events); - size_t offset = 0, nextEventIndex = 0; - while (offset < length) { - size_t nextEventOffset = length; - const clap_event_header *event = nullptr; - if (nextEventIndex < inputEventCount) { - event = process->in_events->get(process->in_events, nextEventIndex); - nextEventOffset = std::max(offset, event->time); - } - - // process up until the next event (or end) - if (nextEventOffset > offset) { - auto delta = nextEventOffset - offset; - plugin.effect.process(inputBuffers.data(), outputBuffers.data(), delta); - offset = nextEventOffset; - for (auto &p : inputBuffers) p += delta; - for (auto &p : outputBuffers) p += delta; - } - if (event) { - plugin.processEvent(event); - ++nextEventIndex; - } - } - inputBuffers.resize(0); - outputBuffers.resize(0); - - if (plugin.effect.hasPendingWebMessage()) { - plugin.host->request_callback(plugin.host); - } - plugin.sendParamEvents(process->out_events); - return CLAP_PROCESS_CONTINUE; - } - static void plugin_on_main_thread(const clap_plugin *obj) { - auto &plugin = *(Plugin *)obj; - plugin.sendWebMessages(); - } - - // parameters - struct Param : public clap_param_info { - typename Effect::ParamRange *rangeParam = nullptr; - std::optional rangeInfo; - typename Effect::ParamStepped *steppedParam = nullptr; - std::optional steppedInfo; - - std::atomic_flag hostValueSent = ATOMIC_FLAG_INIT; - std::atomic_flag hostStartGestureSent = ATOMIC_FLAG_INIT; - std::atomic_flag hostStopGestureSent = ATOMIC_FLAG_INIT; - - Param() {} - Param(Param &&other) : clap_param_info(other), rangeParam(other.rangeParam), rangeInfo(std::move(other.rangeInfo)), steppedParam(other.steppedParam), steppedInfo(std::move(other.steppedInfo)) {} - - void setClapInfo() { - flags |= CLAP_PARAM_IS_AUTOMATABLE; - cookie = this; - if (rangeParam) { - std::strncpy(name, rangeInfo->name.c_str(), CLAP_NAME_SIZE); - // STFX range params are mapped to [0, 1] so we can give them a nonlinear shape - min_value = 0; - max_value = 1; - default_value = rangeInfo->toUnit(rangeInfo->defaultValue); - } else { - std::strncpy(name, steppedInfo->name.c_str(), CLAP_NAME_SIZE); - flags |= CLAP_PARAM_IS_STEPPED; - min_value = steppedInfo->low; - max_value = steppedInfo->high; - default_value = steppedInfo->defaultValue; - } - - hostValueSent.test_and_set(); - hostStartGestureSent.test_and_set(); - hostStopGestureSent.test_and_set(); - } - }; - std::vector params; - struct ParamScanner : public storage::STFXStorageScanner { - std::vector ¶ms; - Crc32 crc; - - ParamScanner(std::vector ¶ms) : params(params) {} - - template - RangeParamInfo & range(const char *key, PR ¶m) { - param.context.index = params.size(); // so we can find it in the listeners below - params.emplace_back(); - auto &entry = params.back(); - entry.id = crc.copy().addString(key, true).done(); - entry.rangeParam = ¶m; - return entry.rangeInfo.emplace(param); - } - template - SteppedParamInfo & stepped(const char *key, PS ¶m) { - param.context.index = params.size(); // so we can find it in the listeners below - - params.emplace_back(); - auto &entry = params.back(); - entry.id = crc.copy().addString(key, true).done(); - entry.steppedParam = ¶m; - return entry.steppedInfo.emplace(param); - } - }; - void scanParams() { - params.clear(); - ParamScanner scanner{params}; - effect.state(scanner); - for (auto &entry : params) entry.setClapInfo(); - - // Listen for changes from the effect UI - effect.paramListenerRange = [&](stfx::web::ParamContext context, double value){ - params[context.index].hostValueSent.clear(); - if (hostParams) hostParams->request_flush(host); - }; - effect.paramListenerGesture = [&](stfx::web::ParamContext context, bool gesture){ - if (gesture) { - params[context.index].hostStartGestureSent.clear(); - } else { - params[context.index].hostStopGestureSent.clear(); - } - if (hostParams) hostParams->request_flush(host); - }; - effect.paramListenerStepped = [&](stfx::web::ParamContext context, int value){ - params[context.index].hostValueSent.clear(); - if (hostParams) hostParams->request_flush(host); - }; - } - void sendParamEvents(const clap_output_events *events) { - for (auto ¶m : params) { - auto sendGesture = [&](uint16_t eventType){ - clap_event_param_gesture event{ - .header={ - .size=sizeof(event), - .time=0, - .space_id=CLAP_CORE_EVENT_SPACE_ID, - .type=eventType - }, - .param_id=param.id, - }; - // Not *super* bothered if this fails - events->try_push(events, &event.header); - }; - - if (!param.hostStartGestureSent.test_and_set()) { - sendGesture(CLAP_EVENT_PARAM_GESTURE_BEGIN); - } - if (!param.hostValueSent.test_and_set()) { - clap_event_param_value event{ - .header={ - .size=sizeof(event), - .time=0, - .space_id=CLAP_CORE_EVENT_SPACE_ID, - .type=CLAP_EVENT_PARAM_VALUE - }, - .param_id=param.id, - .cookie=param.cookie, - .note_id=-1, - .port_index=-1, - .channel=-1, - .key=-1 - }; - if (param.rangeParam) { - event.value = param.rangeInfo->toUnit((double)*param.rangeParam); - } else { - event.value = (int)*param.steppedParam; - } - if (!events->try_push(events, &event.header)) { - // failed, try again later - param.hostValueSent.clear(); - continue; - } - } - if (!param.hostStopGestureSent.test_and_set()) { - sendGesture(CLAP_EVENT_PARAM_GESTURE_END); - } - } - } - - // CLAP parameter methods - static uint32_t params_count(const clap_plugin *obj) { - auto &plugin = *(Plugin *)obj; - return plugin.params.size(); - } - static bool params_get_info(const clap_plugin *obj, uint32_t index, clap_param_info *info) { - auto &plugin = *(Plugin *)obj; - if (index >= plugin.params.size()) return false; - *info = plugin.params[index]; - return true; - } - static bool params_get_value(const clap_plugin *obj, clap_id paramId, double *value) { - auto &plugin = *(Plugin *)obj; - for (auto ¶m : plugin.params) { - if (param.id == paramId) { - if (param.rangeParam) { - *value = param.rangeInfo->toUnit((double)*param.rangeParam); - } else { - *value = (int)*param.steppedParam; - } - param.hostValueSent.test_and_set(); - return true; - } - } - return false; - } - static bool params_value_to_text(const clap_plugin *obj, clap_id paramId, double value, char *text, uint32_t textCapacity) { - auto &plugin = *(Plugin *)obj; - for (auto ¶m : plugin.params) { - if (param.id == paramId) { - if (param.rangeParam) { - auto str = param.rangeInfo->toString(param.rangeInfo->fromUnit(value)); - std::strncpy(text, str.c_str(), textCapacity); - } else { - auto str = param.steppedInfo->toString(int(std::round(value))); - std::strncpy(text, str.c_str(), textCapacity); - } - return true; - } - } - return false; - } - static bool params_text_to_value(const clap_plugin *obj, clap_id paramId, const char *text, double *value) { - auto &plugin = *(Plugin *)obj; - for (auto ¶m : plugin.params) { - if (param.id == paramId) { - std::string str = text; - if (param.rangeParam) { - *value = param.rangeInfo->toUnit(param.rangeInfo->fromString(str)); - } else { - *value = param.steppedInfo->fromString(str); - } - return true; - } - } - return false; - } - static void params_flush(const clap_plugin *obj, const clap_input_events *inEvents, const clap_output_events *outEvents) { - auto &plugin = *(Plugin *)obj; - auto count = inEvents->size(inEvents); - for (uint32_t i = 0; i < count; ++i) { - auto *header = inEvents->get(inEvents, i); - plugin.processEvent(header); - } - plugin.sendParamEvents(outEvents); - } - - // CLAP audio port methods - static uint32_t audio_ports_count(const clap_plugin *obj, bool inputPorts) { - auto &plugin = *(Plugin *)obj; - if (!plugin.isConfigured) { - plugin.isConfigured = plugin.effect.configurePersistent(); - if (!plugin.isConfigured) return 0; - } - auto &config = plugin.effect.config; - if (inputPorts) { - return config.auxInputs.size() + (config.inputChannels > 0); - } else { - return config.auxOutputs.size() + (config.outputChannels > 0); - } - } - static bool audio_ports_get(const clap_plugin *obj, uint32_t index, bool inputPorts, clap_audio_port_info *info) { - auto &plugin = *(Plugin *)obj; - if (!plugin.isConfigured) return false; - auto &config = plugin.effect.config; - - clap_id portIdBase = (inputPorts ? 0x1000000 : 0x2000000); - auto main = uint32_t(inputPorts ? config.inputChannels : config.outputChannels); - auto &aux = (inputPorts ? config.auxInputs : config.auxOutputs); - auto auxIndex = index; - if (main) { - if (index == 0) { - *info = { - .id=portIdBase, - .name={'m', 'a', 'i', 'n'}, - .flags=CLAP_AUDIO_PORT_IS_MAIN, - .channel_count=main, - .port_type=nullptr, - .in_place_pair=CLAP_INVALID_ID - }; - return true; - } - --auxIndex; - } - if (auxIndex < aux.size()) { - *info = { - .id=portIdBase + index, - .name={'a', 'u', 'x'}, - .flags=CLAP_AUDIO_PORT_IS_MAIN, - .channel_count=main, - .port_type=nullptr, - .in_place_pair=CLAP_INVALID_ID - }; - if (aux.size() > 1) { - info->name[3] = '1' + auxIndex; - } - return true; - } - return false; - } - - static bool writeToStream(const unsigned char *bytes, size_t length, const clap_ostream *stream) { - size_t index = 0; - while (index < length) { - size_t remaining = length - index; - auto written = stream->write(stream, bytes + index, remaining); - if (written <= 0) return false; - index += written; - } - return true; - } - static bool fillFromStream(const clap_istream *stream, std::vector &buffer) { - buffer.resize(0); - size_t chunkSize = 1024; - size_t length = 0; - while (1) { - buffer.resize(length + chunkSize); - auto read = stream->read(stream, buffer.data() + length, chunkSize); - length += read; - if (read < 0) { - buffer.resize(0); - return false; - } else if (read == 0) { - buffer.resize(length); - return true; - } - chunkSize = buffer.size(); - } - } - - std::vector stateBuffer; - static bool state_save(const clap_plugin *obj, const clap_ostream *stream) { - auto &plugin = *(Plugin *)obj; - auto &buffer = plugin.stateBuffer; - plugin.effect.saveState(buffer); - return writeToStream(buffer.data(), buffer.size(), stream); - } - static bool state_load(const clap_plugin *obj, const clap_istream *stream) { - auto &plugin = *(Plugin *)obj; - auto &buffer = plugin.stateBuffer; - if (!fillFromStream(stream, buffer)) return false; - plugin.effect.loadState(buffer); - plugin.sendWebMessages(); // should already be on the main thread - return true; - } - - static bool webview1_provide_starting_uri(const clap_plugin_t *obj, char *startingUri, uint32_t capacity) { - auto &plugin = *(Plugin *)obj; - if (!plugin.hostWebview1) return false; - if (!plugin.effect.webPage.size() + 1 > capacity) return false; - std::strcpy(startingUri, plugin.effect.webPage.c_str()); - return true; - } - static bool webview1_receive(const clap_plugin_t *obj, const void *buffer, uint32_t size) { - auto &plugin = *(Plugin *)obj; - plugin.effect.webReceive(buffer, size); - // TODO: *double* check we're on the main thread, for safety - plugin.sendWebMessages(); - return true; - } - - void sendWebMessages() { - if (!hostWebview1) return; - while (auto *m = effect.getPendingWebMessage()) { - hostWebview1->send(host, m->bytes.data(), m->bytes.size()); - m->sent(); - } - } -}; - -}} // namespace - -extern stfx::clap::Plugins stfxPlugins; diff --git a/stfx/param-info.h b/stfx/param-info.h deleted file mode 100644 index 983d7d1..0000000 --- a/stfx/param-info.h +++ /dev/null @@ -1,336 +0,0 @@ -#ifndef SIGNALSMITH_STFX_STFX2_PARAM_INFO_H -#define SIGNALSMITH_STFX_STFX2_PARAM_INFO_H - -#include -#include -#include -#include -#include -#include - -namespace stfx { - class UnitRangeMap { - double a, b, d; - public: - UnitRangeMap() : a(0), b(1), d(0) {} // identity - UnitRangeMap(double min, double mid, double max) { - double k = (mid - min)/(max - mid); - a = min; - b = max*k - min; - d = k - 1; - } - - double toUnit(double value) const { - return (value - a)/(b - value*d); - } - double fromUnit(double unit) const { - return (a + unit*b)/(1 + unit*d); - } - }; - - struct RangeParamInfo { - double defaultValue; - double low, mid, high; - std::string name, description; - - RangeParamInfo(double defaultValue) : defaultValue(defaultValue), low(defaultValue), mid(defaultValue), high(defaultValue) {} - RangeParamInfo(const RangeParamInfo &other) = delete; - RangeParamInfo(RangeParamInfo &&other) = default; - - RangeParamInfo & info(std::string pName, std::string pDescription) { - name = pName; - description = pDescription; - return *this; - } - RangeParamInfo & range(double pLow, double pHigh) { - return range(pLow, pHigh, (pLow + pHigh)*0.5); - } - RangeParamInfo & range(double pLow, double pMid, double pHigh) { - low = pLow; - mid = pMid; - high = pHigh; - // sanity check in case we mix the argument order up - if ((mid < high) != (low < high)) std::swap(mid, high); // middle crosses the high - if ((low < mid) != (low < high)) std::swap(low, mid); // middle crosses the low - rangeMap = {low, mid, high}; - return *this; - } - - typedef double((*DoubleMap)(double)); - RangeParamInfo & unit(std::string suffix, int precision=2, DoubleMap fromDisplay=identityMap, DoubleMap toDisplay=identityMap, double validLow=doubleLowest, double validHigh=doubleMax) { - unitOptions.emplace_back(suffix, fromDisplay, toDisplay, validLow, validHigh, precision); - return *this; - } - RangeParamInfo & unit(std::string suffix, DoubleMap fromDisplay, DoubleMap toDisplay, double validLow=doubleLowest, double validHigh=doubleMax) { - return unit(suffix, 2, fromDisplay, toDisplay, validLow, validHigh); - } - RangeParamInfo & unit(std::string suffix, int precision, double validLow, double validHigh) { - return unit(suffix, precision, identityMap, identityMap, validLow, validHigh); - } - RangeParamInfo & unit(std::string suffix, double validLow, double validHigh) { - return unit(suffix, identityMap, identityMap, validLow, validHigh); - } - - RangeParamInfo & exact(double v, std::string valueName) { - exactOptions.push_back({v, valueName}); - return *this; - } - - std::string toString(double value) const { - for (auto &option : exactOptions) { - if (value == option.value) return option.name; - } - for (const auto &unit : unitOptions) { - if (unit.valid(value)) return unit.toString(value); - } - if (unitOptions.empty()) return std::to_string(value); - return unitOptions[0].toString(value); - } - void toString(double value, std::string &valueString, std::string &unitString) const { - for (auto &option : exactOptions) { - if (value == option.value) { - valueString = option.name; - unitString = ""; - return; - } - } - for (const auto &unit : unitOptions) { - if (unit.valid(value)) return unit.toString(value, valueString, unitString); - } - if (unitOptions.empty()) { - valueString = std::to_string(value); - unitString = ""; - } - unitOptions[0].toString(value, valueString, unitString); - } - double fromString(const std::string &str) const { - bool hasDecimal = false, hasDigit = false;; - { // check for a parseable number - size_t pos = 0; - while (str[pos] == '\t' || str[pos] == ' ') ++pos; // strip leading whitespace - if (str[pos] == '-') ++pos; - while (pos < str.length()) { - if (!hasDecimal && (str[pos] == '.' || str[pos] == ',')) { - hasDecimal = true; - } else if (str[pos] >= '0' && str[pos] <= '9') { - hasDigit = true; - } else { - break; - } - ++pos; - } - } - // It's not a number - look for an exact match - if (!hasDigit) { - int longestMatch = -1; - double result = defaultValue; - for (auto &option : exactOptions) { - for (size_t i = 0; i < option.name.size() && i < str.size(); ++i) { - if (option.name[i] == str[i]) { - if (int(i) > longestMatch) { - longestMatch = i; - result = option.value; - } - } else { - break; - } - } - } - return result; - } - - size_t pos = 0; - double result = std::stod(str, 0); - // Skip whitespace after the number - while (pos < str.length() && (str[pos] == ' ' || str[pos] == '\t')) ++pos; - size_t end = str.length(); // and at the end - while (end > pos && end > 0 && (str[end - 1] == ' ' || str[end - 1] == '\t')) --end; - std::string unit = str.substr(pos, end - pos); - - for (const auto &u : unitOptions) { - if (u.unit == unit) { - return u.fromDisplay(result); - } - } - return result; - } - - std::vector getUnits() const { - std::vector result; - for (size_t i = 0; i < unitOptions.size(); ++i) { - bool duplicate = false; - for (size_t j = 0; j < i; ++j) { - if (unitOptions[i].unit == unitOptions[j].unit) { - duplicate = true; - break; - } - } - if (!duplicate) result.push_back(unitOptions[i].unit); - } - return result; - } - - double toUnit(double value) const { - return rangeMap.toUnit(value); - } - double fromUnit(double unit) const { - return rangeMap.fromUnit(unit); - } - - private: - static constexpr double doubleLowest = std::numeric_limits::lowest(); - static constexpr double doubleMax = std::numeric_limits::max(); - static double identityMap(double v) { - return v; - } - - // A map from [0, 1] to another range with specified midpoint, based on a 1/x curve - UnitRangeMap rangeMap; - - protected: - struct ExactEntry { - double value; - std::string name; - }; - std::vector exactOptions; - struct UnitEntry { - std::string fixedDisplay = ""; - std::string unit; - bool addSpace = false, useFixed = false, keepZeros = false; - DoubleMap fromDisplay, toDisplay; - double validLow, validHigh; - int precision; - double precisionOffset = 0; - - UnitEntry(std::string unit, DoubleMap fromDisplay, DoubleMap toDisplay, double validLow, double validHigh, int precision=2) : unit(unit), fromDisplay(fromDisplay), toDisplay(toDisplay), validLow(validLow), validHigh(validHigh), precision(precision) { - if (validLow > validHigh) { - std::swap(validLow, validHigh); - keepZeros = true; - } - if (unit[0] == ' ') { - addSpace = true; - this->unit = unit.substr(1, unit.size() - 1); - } - precisionOffset = 0.4999*std::pow(10, -precision); - } - - bool valid(double value) const { - return value >= validLow && value <= validHigh; - } - - void toString(double value, std::string &valueString, std::string &unitString) const { - std::ostringstream oss; - oss.precision(precision); - oss << std::fixed; - double offset = 0; - if (precision > 0) { - oss << toDisplay(value) + offset; - } else { - oss << (int)(toDisplay(value) + offset); - } - valueString = oss.str(); - // Strip trailing zeroes - if (!keepZeros) for (int i = 0; i < (int)valueString.size(); ++i) { - if (valueString[i] == '.') { - int zeros = valueString.size(); - while (zeros > i && valueString[zeros - 1] == '0') --zeros; - if (zeros == i + 1) --zeros; - valueString = valueString.substr(0, zeros); - break; - } - } - unitString = unit; - } - std::string toString(double value) const { - std::string valueString, unitString; - toString(value, valueString, unitString); - return valueString + (addSpace ? " " : "") + unitString; - } - }; - std::vector unitOptions; - }; - - struct SteppedParamInfo { - int defaultValue; - int low, high; - std::string name, description; - - SteppedParamInfo(int defaultValue) : defaultValue(defaultValue), low(defaultValue), high(defaultValue), nameIndex(defaultValue) {} - SteppedParamInfo(const SteppedParamInfo &other) = delete; - SteppedParamInfo(SteppedParamInfo &&other) = default; - - SteppedParamInfo & info(std::string pName, std::string pDescription) { - name = pName; - description = pDescription; - return *this; - } - SteppedParamInfo & range(int pLow, int pHigh) { - low = pLow; - high = pHigh; - return *this; - } - - SteppedParamInfo & label(const char *n) { - if (nameIndex > high && high >= low) high = nameIndex; - if (nameIndex > low && low > high) low = nameIndex; - nameMap[nameIndex] = n; - valueMap[n] = nameIndex; - return *this; - } - bool fullyLabelled() const { - return int(nameMap.size()) == (high - low + 1); - } - - template - SteppedParamInfo & label(const char *first, Args... others) { - label(first); - ++nameIndex; - return label(others...); - } - - template - SteppedParamInfo & label(int start, const char *first, Args... others) { - nameIndex = start; - return label(first, others...); - } - - const std::string * getLabel(int value) const { - auto pair = nameMap.find(value); - if (pair != nameMap.end()) { - return &pair->second; - } - return nullptr; - } - - std::string toString(int value) const { - const std::string *maybeLabel = getLabel(value); - if (maybeLabel) return *maybeLabel; - return std::to_string(value); - } - int fromString(const std::string &str) const { - auto pair = valueMap.find(str); - if (pair != valueMap.end()) return pair->second; - - size_t pos = 0; - if (str[0] == '-') ++pos; - while (pos < str.length() && str[pos] >= '0' && str[pos] <= '9') ++pos; - if (pos >= str.length() || pos == 0 || (pos == 1 && str[0] == '-')) { - return defaultValue; - } - int result = std::stoi(str, &pos); - if (high >= low) { - return std::max(std::min(high, result), low); - } else { - return std::max(std::min(low, result), high); - } - } - - private: - int nameIndex; - protected: - std::map nameMap; - std::map valueMap; - }; - -} // namespace -#endif // include guard diff --git a/stfx/storage/cbor-walker.h b/stfx/storage/cbor-walker.h deleted file mode 100644 index 1e2da49..0000000 --- a/stfx/storage/cbor-walker.h +++ /dev/null @@ -1,990 +0,0 @@ -#ifndef SIGNALSMITH_CBOR_WALKER_H -#define SIGNALSMITH_CBOR_WALKER_H - -#include -#include -#include -#ifndef UINT64_MAX -# define UINT64_MAX 0xFFFFFFFFFFFFFFFFull; -#endif - -#include -#include -#if __cplusplus >= 201703L -# define CBOR_WALKER_USE_STRING_VIEW -# include -#endif -#if __cplusplus >= 202002L -# define CBOR_WALKER_USE_BIT_CAST -# include -#endif - -namespace signalsmith { namespace cbor { - -struct CborWalker { - CborWalker(uint64_t errorCode=ERROR_NOT_INITIALISED) : CborWalker(nullptr, nullptr, errorCode) {} - CborWalker(const std::vector &vector) : CborWalker(vector.data(), vector.data() + vector.size()) {} - CborWalker(const unsigned char *data, const unsigned char *dataEnd) : data(data), dataEnd(dataEnd) { - if (data >= dataEnd) { - typeCode = TypeCode::error; - additional = ERROR_END_OF_DATA; - return; - } - unsigned char head = *data; - typeCode = (TypeCode)(head>>5); - unsigned char remainder = head&0x1F; - switch (remainder) { - case 24: - additional = data[1]; - dataNext = data + 2; - break; - case 25: - if (typeCode == TypeCode::simple) { -#ifdef CBOR_WALKER_HALF_PRECISION_FLOAT - // Translated from RFC 8949 Appendix D - uint16_t half = ((uint16_t)data[1]<<8) + data[2]; - uint16_t exponent = (half>>10)&0x001F; - uint16_t mantissa = half&0x03FF; - double value; - if (exponent == 0) { - value = std::ldexpf(mantissa, -24); - } else if (exponent == 31) { - value = (mantissa == 0) ? INFINITY : NAN; - } else { - value = std::ldexpf(mantissa + 1024, exponent - 25); - } - typeCode = TypeCode::float32; - float32 = (half&0x8000) ? -value : value; -#else - float32 = 0; -#endif - } else { - additional = (uint64_t(data[1])<<8)|uint64_t(data[2]); - } - dataNext = data + 3; - break; - case 26: - if (typeCode == TypeCode::simple) { - typeCode = TypeCode::float32; - } - additional = (uint64_t(data[1])<<24)|(uint64_t(data[2])<<16)|(uint64_t(data[3])<<8)|uint64_t(data[4]); - dataNext = data + 5; - break; - case 27: - if (typeCode == TypeCode::simple) { - typeCode = TypeCode::float64; - } - additional = (uint64_t(data[1])<<56)|(uint64_t(data[2])<<48)|(uint64_t(data[3])<<40)|(uint64_t(data[4])<<32)|(uint64_t(data[5])<<24)|(uint64_t(data[6])<<16)|(uint64_t(data[7])<<8)|uint64_t(data[8]); - dataNext = data + 9; - break; - case 28: - case 29: - case 30: - typeCode = TypeCode::error; - additional = ERROR_INVALID_ADDITIONAL; - dataNext = data; - case 31: - additional = 0; // returns 0 length for indefinite values - switch (typeCode) { - case TypeCode::integerP: - case TypeCode::integerN: - case TypeCode::tag: - typeCode = TypeCode::error; - additional = ERROR_INVALID_ADDITIONAL; - break; - case TypeCode::bytes: - typeCode = TypeCode::indefiniteBytes; - break; - case TypeCode::utf8: - typeCode = TypeCode::indefiniteUtf8; - break; - case TypeCode::array: - typeCode = TypeCode::indefiniteArray; - break; - case TypeCode::map: - typeCode = TypeCode::indefiniteMap; - break; - case TypeCode::simple: - typeCode = TypeCode::indefiniteBreak; - break; - default: - typeCode = TypeCode::error; - additional = ERROR_SHOULD_BE_IMPOSSIBLE; - break; - } - default: - additional = remainder; - dataNext = data + 1; - break; - } - } - - // All error codes are non-zero, so can be checked with `.error()` - static constexpr uint64_t ERROR_END_OF_DATA = 1; - static constexpr uint64_t ERROR_INVALID_ADDITIONAL = 2; - static constexpr uint64_t ERROR_INVALID_VALUE = 3; - static constexpr uint64_t ERROR_INCONSISTENT_INDEFINITE = 4; - static constexpr uint64_t ERROR_NOT_INITIALISED = 5; - static constexpr uint64_t ERROR_METHOD_TYPE_MISMATCH = 6; - static constexpr uint64_t ERROR_SHOULD_BE_IMPOSSIBLE = 7; - - CborWalker next(size_t count) const { - CborWalker result = *this; - for (size_t i = 0; i < count; ++i) { - ++result; - } - return result; - } - CborWalker next() const { - switch (typeCode) { - case TypeCode::integerP: - case TypeCode::integerN: - case TypeCode::simple: - case TypeCode::float32: - case TypeCode::float64: - case TypeCode::indefiniteBreak: - return nextBasic(); - case TypeCode::bytes: - case TypeCode::utf8: - return {dataNext + additional, dataEnd}; - return {dataNext + additional, dataEnd}; - case TypeCode::array: { - auto result = nextBasic(); - auto length = additional; - for (uint64_t i = 0; i < length; ++i) { - ++result; - } - return result; - } - case TypeCode::map: { - auto result = nextBasic(); - auto length = additional; - for (uint64_t i = 0; i < length; ++i) { - ++result; - ++result; - } - return result; - } - case TypeCode::indefiniteBytes: { - auto result = nextBasic(); - while (!result.error() && result.typeCode != TypeCode::indefiniteBreak) { - if (result.typeCode != TypeCode::bytes) { - return {data, dataEnd, ERROR_INCONSISTENT_INDEFINITE}; - } - ++result; - } - return result.nextBasic(); - } - case TypeCode::indefiniteUtf8: { - auto result = nextBasic(); - while (!result.error() && result.typeCode != TypeCode::indefiniteBreak) { - if (result.typeCode != TypeCode::utf8) { - return {data, dataEnd, ERROR_INCONSISTENT_INDEFINITE}; - } - ++result; - } - return result.nextBasic(); - } - case TypeCode::indefiniteArray: { - auto result = nextBasic(); - while (!result.error() && result.typeCode != TypeCode::indefiniteBreak) { - result = result.next(); - } - return result.nextBasic(); - } - case TypeCode::indefiniteMap: { - auto result = nextBasic(); - while (!result.error() && result.typeCode != TypeCode::indefiniteBreak) { - ++result; - ++result; - } - return result.nextBasic(); - } - case TypeCode::tag: { - // Skip all the tags first - auto result = nextBasic(); - while (result.isTagged()) result = nextBasic(); - return result.next(); - } - case TypeCode::error: - return *this; - } - } - - // ++Prefix increments the position, and returns itself - CborWalker & operator++() { - *this = next(); - return *this; - } - - // Postfix++ increments the position, but returns the old position - CborWalker operator++(int) { - CborWalker result = *this; - *this = next(); - return result; - } - - CborWalker enter() const { - switch (typeCode) { - case TypeCode::integerP: - case TypeCode::integerN: - case TypeCode::simple: - case TypeCode::float32: - case TypeCode::float64: - case TypeCode::indefiniteBreak: - case TypeCode::bytes: - case TypeCode::utf8: - return next(); - case TypeCode::tag: - case TypeCode::array: - case TypeCode::map: - case TypeCode::indefiniteBytes: - case TypeCode::indefiniteUtf8: - case TypeCode::indefiniteArray: - case TypeCode::indefiniteMap: - return nextBasic(); - case TypeCode::error: - return *this; - } - } - - CborWalker nextExit() const { - CborWalker result = *this; - while (!result.error() && !result.isExit()) { - ++result; - } - return result.nextBasic(); - } - - uint64_t error() const { - return typeCode == TypeCode::error ? additional : 0; - } - - bool isSimple() const { - return typeCode == TypeCode::simple; - } - bool isBool() const { - if (typeCode != TypeCode::simple) return false; - return (additional == 20 || additional == 21); - } - explicit operator bool() const { - return (additional == 21); - } - - bool isNull() const { - return typeCode == TypeCode::simple && additional == 22; - } - - bool isUndefined() const { - return typeCode == TypeCode::simple && additional == 23; - } - - bool isExit() const { - return typeCode == TypeCode::indefiniteBreak; - } - - bool atEnd() const { - return typeCode == TypeCode::error && additional == ERROR_END_OF_DATA; - } - - bool isNumber() const { - return isFloat() || isInt(); - } - - bool isInt() const { - return typeCode == TypeCode::integerP || typeCode == TypeCode::integerN; - } - operator uint64_t() const { - switch (typeCode) { - case TypeCode::integerP: - case TypeCode::bytes: - case TypeCode::utf8: - case TypeCode::array: - case TypeCode::map: - case TypeCode::tag: - case TypeCode::simple: - case TypeCode::error: - return (int64_t)additional; - case TypeCode::integerN: - return (uint64_t)-1 - (uint64_t)additional; - return additional; - case TypeCode::float32: - return (uint64_t)float32; - case TypeCode::float64: - return (uint64_t)float64; - default: - return 0; - } - } - operator int64_t() const { - switch (typeCode) { - case TypeCode::integerP: - case TypeCode::bytes: - case TypeCode::utf8: - case TypeCode::array: - case TypeCode::map: - case TypeCode::tag: - case TypeCode::simple: - case TypeCode::error: - return (int64_t)additional; - case TypeCode::integerN: - return -1 - (int64_t)additional; - case TypeCode::float32: - return (uint64_t)float32; - case TypeCode::float64: - return (uint64_t)float64; - default: - return 0; - } - } - operator uint32_t() const { - return (uint32_t)(uint64_t)(*this); - } - operator uint16_t() const { - return (uint16_t)(uint64_t)(*this); - } - operator uint8_t() const { - return (uint32_t)(uint64_t)(*this); - } - operator size_t() const { - return (size_t)(uint64_t)(*this); - } - // For the signed ones, we cast from the signed 64-bit - operator int32_t() const { - return (int32_t)(int64_t)(*this); - } - operator int16_t() const { - return (int16_t)(int64_t)(*this); - } - operator int8_t() const { - return (int8_t)(int64_t)(*this); - } - - bool isFloat() const { - return typeCode == TypeCode::float32 || typeCode == TypeCode::float64; - } - operator double() const { - switch (typeCode) { - case TypeCode::float32: - return float32; - case TypeCode::float64: - return float64; - case TypeCode::integerP: - return (uint64_t)(*this); - case TypeCode::integerN: - return (int64_t)(*this); - default: - return 0; - } - } - operator float() const { - return (float)(double)(*this); - } - - bool isBytes() const { - return typeCode == TypeCode::bytes || typeCode == TypeCode::indefiniteBytes; - } - bool isUtf8() const { - return typeCode == TypeCode::utf8 || typeCode == TypeCode::indefiniteUtf8; - } - bool hasLength() const { - return typeCode != TypeCode::indefiniteBytes && typeCode != TypeCode::indefiniteUtf8 && typeCode != TypeCode::indefiniteArray && typeCode != TypeCode::indefiniteMap; - } - size_t length() const { - return (size_t)(*this); - } - - const unsigned char * bytes() const { - return dataNext; - } - - std::string utf8() const { - if (typeCode != TypeCode::utf8) return ""; - return {(const char *)dataNext, length()}; - } -#ifdef CBOR_WALKER_USE_STRING_VIEW - std::string_view utf8View() const { - if (typeCode != TypeCode::utf8) return {nullptr, 0}; - return {(const char *)dataNext, length()}; - } -#endif - - bool isArray() const { - return typeCode == TypeCode::array || typeCode == TypeCode::indefiniteArray; - } - template - CborWalker forEach(Fn &&fn, bool mapValues=true) const { - if (typeCode == TypeCode::array) { - size_t count = length(); - CborWalker item = enter(); - for (size_t i = 0; i < count; ++i) { - if (item.error()) return item; - CborWalker value = item++; - fn(value, i); - } - return item; - } else if (typeCode == TypeCode::indefiniteArray) { - CborWalker item = enter(); - size_t i = 0; - while (!item.error() && !item.isExit()) { - fn(item++, i++); - } - return item.next(); // move past the exit - } else if (typeCode == TypeCode::indefiniteBytes) { - CborWalker item = enter(); - size_t i = 0; - while (!item.error() && !item.isExit()) { - if (item.typeCode != TypeCode::bytes) return {data, dataEnd, ERROR_INVALID_VALUE}; - fn(item++, i++); - } - return item.next(); // move past the exit - } else if (typeCode == TypeCode::indefiniteUtf8) { - CborWalker item = enter(); - size_t i = 0; - while (!item.error() && !item.isExit()) { - if (item.typeCode != TypeCode::utf8) return {data, dataEnd, ERROR_INVALID_VALUE}; - fn(item++, i++); - } - return item.next(); // move past the exit - } else if (typeCode == TypeCode::map) { - size_t count = length(); - CborWalker item = enter(); - for (size_t i = 0; i < count; ++i) { - if (item.error()) return item; - CborWalker key = item++; - if (item.error()) return item; - CborWalker value = item++; - fn(mapValues ? value : key, i); - } - return item; - } else if (typeCode == TypeCode::indefiniteMap) { - CborWalker item = enter(); - size_t i = 0; - while (!item.error() && !item.isExit()) { - CborWalker key = item++; - if (item.error()) return item; - if (item.isExit()) return {item.data, item.dataEnd, ERROR_INVALID_VALUE}; - CborWalker value = item++; - fn(mapValues ? value : key, i); - ++i; - } - return item.next(); // move past the exit - } - return {data, dataEnd, ERROR_METHOD_TYPE_MISMATCH}; - } - - bool isMap() const { - return typeCode == TypeCode::map || typeCode == TypeCode::indefiniteMap; - } - - template - CborWalker forEachPair(Fn &&fn) const { - if (typeCode == TypeCode::map) { - size_t count = length(); - CborWalker item = enter(); - for (size_t i = 0; i < count; ++i) { - auto key = item++; - if (key.error() || item.error()) return item; - auto value = item++; - fn(key, value); - } - return item; - } else if (typeCode == TypeCode::indefiniteMap) { - CborWalker item = enter(); - while (!item.error() && !item.isExit()) { - auto key = item++; - if (key.error() || item.error()) return item; - if (item.isExit()) return {item.data, item.dataEnd, ERROR_INVALID_VALUE}; - auto value = item++; - fn(CborWalker(key), CborWalker(value)); - } - return item.next(); // move past the exit - } - return {data, dataEnd, ERROR_METHOD_TYPE_MISMATCH}; - } - - bool isEnd() const { - return typeCode == TypeCode::array || typeCode == TypeCode::indefiniteArray; - } - - bool isTagged() const { - return typeCode == TypeCode::tag; - } - -protected: - CborWalker(const unsigned char *data, const unsigned char *dataEnd, uint64_t errorCode) : data(data), dataEnd(dataEnd), dataNext(nullptr), typeCode(TypeCode::error), additional(errorCode) {} - - // The next *core* value - but doesn't check whether the current value is the header for a string/array/etc. - CborWalker nextBasic() const { - return {dataNext, dataEnd}; - } - - const unsigned char *data, *dataEnd, *dataNext; - enum class TypeCode { - integerP, integerN, bytes, utf8, array, map, tag, simple, float32, float64, - error, indefiniteBreak, indefiniteBytes, indefiniteUtf8, indefiniteArray, indefiniteMap - }; - TypeCode typeCode; - union { - uint64_t additional; - float float32; - double float64; - unsigned char additionalBytes[8]; - }; -}; - -// Automatically skips over tags, but still lets you query them -struct TaggedCborWalker : public CborWalker { - TaggedCborWalker() {} - TaggedCborWalker(const CborWalker& basic) : CborWalker(basic), tagStart(data) { - consumeTags(); - } - TaggedCborWalker(const unsigned char *dataStart, const unsigned char *dataEnd) : CborWalker(dataStart, dataEnd), tagStart(dataStart) { - consumeTags(); - } - - TaggedCborWalker next(size_t i=1) const { - return CborWalker::next(i); - } - TaggedCborWalker & operator++() { - CborWalker::operator++(); - return *this; - } - TaggedCborWalker operator++(int _) { - return CborWalker::operator++(_); - } - TaggedCborWalker enter() const { - return CborWalker::enter(); - } - TaggedCborWalker nextExit() const { - return CborWalker::nextExit(); - } - template - TaggedCborWalker forEach(Fn &&fn) const { - return CborWalker::forEach([&](const CborWalker &item, size_t i){ - fn(TaggedCborWalker{item}, i); - }); - } - template - TaggedCborWalker forEachPair(Fn &&fn) const { - return CborWalker::forEachPair([&](const CborWalker &key, const CborWalker &value){ - fn(TaggedCborWalker{key}, TaggedCborWalker{value}); - }); - } - - size_t tagCount() const { - return nTags; - } - - uint64_t tag(size_t tagIndex) const { - CborWalker tagWalker(tagStart, dataEnd); - for (size_t i = 0; i < tagIndex; ++i) { - tagWalker = tagWalker.enter(); - } - return tagWalker; - } - - bool isTypedArray() const { - return isBytes() && typedArrayTag; - } - - size_t typedArrayLength() const { - uint8_t widthLog2 = typedArrayTag&0x03; - uint8_t elementType = (typedArrayTag&0x18)>>3; // unsigned, signed, float - widthLog2 += (elementType == 2); // int sizes are 8-64 bits, float sizes are 16-128 - size_t stride = 1< - size_t readTypedArray(Array &&array) const { - return readTypedArray(array, 0, typedArrayLength()); - } - - template - size_t readTypedArray(Array &&array, size_t offset, size_t maxCount) const { - size_t byteLength = length(); - - bool bigEndian = !(typedArrayTag&0x04); - - switch (typedArrayTag&0xFB) { // without endian flag - // unsigned int - case 64: { - size_t count = std::min(maxCount, byteLength); - const uint8_t *bytes = dataNext + offset; - for (size_t i = 0; i < count; ++i) { - array[i] = bytes[i]; - } - return count; - } - case 65: - return typedArrayReadInner(array, offset, maxCount, bigEndian); - case 66: - return typedArrayReadInner(array, offset, maxCount, bigEndian); - case 67: - return typedArrayReadInner(array, offset, maxCount, bigEndian); - // signed int - case 72: { - size_t count = std::min(maxCount, byteLength); - const uint8_t *bytes = dataNext + offset; - for (size_t i = 0; i < count; ++i) { - array[i] = (int8_t)bytes[i]; // cast to signed here first, to make sure negatives behave correctly - } - return count; - } - case 73: - return typedArrayReadInner(array, offset, maxCount, bigEndian); - case 74: - return typedArrayReadInner(array, offset, maxCount, bigEndian); - case 75: - return typedArrayReadInner(array, offset, maxCount, bigEndian); - // floating-point - case 80: - // TODO: half-precision float support - return 0; - case 81: - return typedArrayReadInner(array, offset, maxCount, bigEndian); - case 82: - return typedArrayReadInner(array, offset, maxCount, bigEndian); - case 83: - // TODO: quad-precision float support - return 0; - default: - return 0; - } - } - -private: - size_t nTags = 0; - const unsigned char *tagStart; - - uint8_t typedArrayTag = 0; - - void consumeTags() { - while (isTagged() && data < dataEnd) { - ++nTags; - uint64_t tag = (*this); - if (tag >= 64 && tag < 87) { // RFC-8746 range - typedArrayTag = tag; - } - // Move "into" the tag - CborWalker::operator=(enter()); - } - } - - template - size_t typedArrayReadInner(Array &&array, size_t offset, size_t maxCount, bool bigEndian) const { - constexpr size_t B = sizeof(UIntType); - if (offset*B > length()) return 0; - const uint8_t *bytes = dataNext + offset*B; - size_t count = std::min(maxCount, length()/B - offset); - if (bigEndian) { - for (size_t i = 0; i < count; ++i) { - UIntType v = 0; - for (size_t b = 0; b < B; ++b) { - UIntType bv = bytes[i*B + b]; - v += bv<<((B - 1 - b)*8); - } - if (bitcast) { -#ifdef CBOR_WALKER_USE_BIT_CAST - array[i] = std::bit_cast(v); -#else - ResultT r; - std::memcpy(&r, &v, B); - array[i] = r; -#endif - } else { - array[i] = (ResultT)v; - } - } - } else { - for (size_t i = 0; i < count; ++i) { - UIntType v = 0; - for (size_t b = 0; b < B; ++b) { - UIntType bv = bytes[i*B + b]; - v += bv<<(b*8); - } - if (bitcast) { -#ifdef CBOR_WALKER_USE_BIT_CAST - array[i] = std::bit_cast(v); -#else - ResultT r; - std::memcpy(&r, &v, B); - array[i] = r; -#endif - } else { - array[i] = (ResultT)v; - } - } - } - return count; - } -}; - -struct CborWriter { - CborWriter(std::vector &bytes) : bytes(bytes) {} - - CborWriter & addUInt(uint64_t u) { - writeHead(0, u); - return *this; - } - CborWriter & addInt(int64_t u) { - if (u >= 0) { - writeHead(0, u); - } else { - writeHead(1, -1 - u); - } - return *this; - } - CborWriter & addTag(uint64_t u) { - writeHead(6, u); - return *this; - } - CborWriter & addBool(bool b) { - writeHead(7, 20 + b); - return *this; - } - CborWriter & openArray() { - bytes.push_back(0x9F); - return *this; - } - CborWriter & openArray(size_t items) { - writeHead(4, items); - return *this; - } - CborWriter & openMap() { - bytes.push_back(0xBF); - return *this; - } - CborWriter & openMap(size_t pairs) { - writeHead(5, pairs); - return *this; - } - CborWriter & close() { - bytes.push_back(0xFF); - return *this; - } - CborWriter & addBytes(const void *ptr, size_t length) { - addBytes((const unsigned char *)ptr, length); - return *this; - } - CborWriter & addBytes(const unsigned char *ptr, size_t length) { - writeHead(2, length); - bytes.insert(bytes.end(), ptr, ptr + length); - return *this; - } - CborWriter & openBytes() { - bytes.push_back(0x5F); - return *this; - } - CborWriter & addUtf8(const char *ptr, size_t length) { - writeHead(3, length); - bytes.insert(bytes.end(), ptr, ptr + length); - return *this; - } - CborWriter & addUtf8(const char *str) { - addUtf8(str, std::strlen(str)); - return *this; - } - CborWriter & addUtf8(const std::string &str) { - addUtf8(str.c_str()); - return *this; - } -#ifdef CBOR_WALKER_USE_STRING_VIEW - CborWriter & addUtf8(const std::string_view &str) { - addUtf8(str.data(), str.size()); - return *this; - } -#endif - CborWriter & openUtf8() { - bytes.push_back(0x7F); - return *this; - } - CborWriter & addNull() { - bytes.push_back(0xF6); - return *this; - } - CborWriter & addUndefined() { - bytes.push_back(0xF7); - return *this; - } - CborWriter & addSimple(unsigned char k) { - writeHead(7, k); - return *this; - } - - CborWriter & addFloat(float v) { - bytes.push_back(0xFA); -#ifdef CBOR_WALKER_USE_BIT_CAST - uint32_t vi = std::bit_cast(v); -#else - uint32_t vi; - std::memcpy(&vi, &v, 4); -#endif - for (size_t i = 0; i < 4; ++i) { - auto shift = (3 - i)*8; - bytes.push_back((vi>>shift)&0xFF); - } - return *this; - } - CborWriter & addFloat(double v) { - bytes.push_back(0xFB); -#ifdef CBOR_WALKER_USE_BIT_CAST - uint64_t vi = std::bit_cast(v); -#else - uint64_t vi; - std::memcpy(&vi, &v, 8); -#endif - for (size_t i = 0; i < 8; ++i) { - auto shift = (7 - i)*8; - bytes.push_back((vi>>shift)&0xFF); - } - return *this; - } - - // RFC-8746 tags for typed arrays - // bits: [1, 0] = log2(elementBytes), [2] = isLittleEndian, [3, 4] = [unsigned, signed, float] - CborWriter & addTypedArray(const uint8_t *arr, size_t length) { - addTag(64); - addBytes((const void *)arr, length); - return *this; - } - CborWriter & addTypedArray(const int8_t *arr, size_t length) { - addTag(72); - addBytes((const void *)arr, length); - return *this; - } - CborWriter & addTypedArray(const uint16_t *arr, size_t length, bool bigEndian=false) { - addTag(bigEndian ? 65 : 69); - writeTypedBlock(arr, length, bigEndian); - return *this; - } - CborWriter & addTypedArray(const uint32_t *arr, size_t length, bool bigEndian=false) { - addTag(bigEndian ? 66 : 70); - writeTypedBlock(arr, length, bigEndian); - return *this; - } - CborWriter & addTypedArray(const uint64_t *arr, size_t length, bool bigEndian=false) { - addTag(bigEndian ? 67 : 71); - writeTypedBlock(arr, length, bigEndian); - return *this; - } - // For signed ints, we make a proxy struct which casts them on-the-fly - CborWriter & addTypedArray(const int16_t *arr, size_t length, bool bigEndian=false) { - addTag(bigEndian ? 73 : 77); - struct { - const int16_t *arr; - uint16_t operator[](size_t i) const { - return (uint16_t)(arr[i]); - } - } unsignedArray{arr}; - writeTypedBlock(unsignedArray, length, bigEndian); - return *this; - } - CborWriter & addTypedArray(const int32_t *arr, size_t length, bool bigEndian=false) { - addTag(bigEndian ? 74 : 78); - struct { - const int32_t *arr; - uint32_t operator[](size_t i) const { - return (uint32_t)(arr[i]); - } - } unsignedArray{arr}; - writeTypedBlock(unsignedArray, length, bigEndian); - return *this; - } - CborWriter & addTypedArray(const int64_t *arr, size_t length, bool bigEndian=false) { - addTag(bigEndian ? 75 : 79); - struct { - const int64_t *arr; - uint64_t operator[](size_t i) const { - return (uint64_t)(arr[i]); - } - } unsignedArray{arr}; - writeTypedBlock(unsignedArray, length, bigEndian); - return *this; - } - CborWriter & addTypedArray(const float *arr, size_t length, bool bigEndian=false) { - addTag(bigEndian ? 81 : 85); - struct { - const float *arr; - uint32_t operator[](size_t i) const { -#ifdef CBOR_WALKER_USE_BIT_CAST - return std::bit_cast(arr[i]); -#else - float v = arr[i]; - uint32_t vi; - std::memcpy(&vi, &v, 4); - return vi; -#endif - } - } unsignedArray{arr}; - writeTypedBlock(unsignedArray, length, bigEndian); - return *this; - } - CborWriter & addTypedArray(const double *arr, size_t length, bool bigEndian=false) { - addTag(bigEndian ? 82 : 86); - struct { - const double *arr; - uint64_t operator[](size_t i) const { -#ifdef CBOR_WALKER_USE_BIT_CAST - return std::bit_cast(arr[i]); -#else - double v = arr[i]; - uint64_t vi; - std::memcpy(&vi, &v, 8); - return vi; -#endif - } - } unsignedArray{arr}; - writeTypedBlock(unsignedArray, length, bigEndian); - return *this; - } - -private: - void writeHead(unsigned char type, uint64_t argument) { - type <<= 5; - if (argument >= 4294967296ul) { - bytes.push_back(type|27); - for (size_t i = 0; i < 8; ++i) { - bytes.push_back(argument>>(56 - i*8)); - } - } else if (argument >= 65536) { - bytes.push_back(type|26); - for (size_t i = 0; i < 4; ++i) { - bytes.push_back(argument>>(24 - i*8)); - } - } else if (argument >= 256) { - bytes.push_back(type|25); - bytes.push_back(argument>>8); - bytes.push_back(argument); - } else if (argument >= 24) { - bytes.push_back(type|24); - bytes.push_back(argument); - } else { - bytes.push_back(type|argument); - } - } - - template - void writeTypedBlock(Array &&array, size_t length, bool bigEndian) { - constexpr size_t B = sizeof(UIntType); - writeHead(2, length*B); - if (bigEndian) { - for (size_t i = 0; i < length; ++i) { - UIntType v = array[i]; - for (size_t b = 0; b < B; ++b) bytes.push_back((v>>((B-1-b)*8))&0xFF); - } - } else { - for (size_t i = 0; i < length; ++i) { - UIntType v = array[i]; - for (size_t b = 0; b < B; ++b) bytes.push_back((v>>(b*8))&0xFF); - } - } - } - - std::vector &bytes; -}; - -}} // namespace - -#endif // include guard diff --git a/stfx/storage/stfx-storage.h b/stfx/storage/stfx-storage.h deleted file mode 100644 index f695d31..0000000 --- a/stfx/storage/stfx-storage.h +++ /dev/null @@ -1,136 +0,0 @@ -#pragma once - -#include "./storage.h" -#include "../stfx-library.h" // for the ...ParamIgnore classes - -namespace stfx { namespace storage { - -template -struct STFXStorageScanner : public signalsmith::storage::StorageScanner { - void info(const char *, const char *) {} - int version(int v) { - return v; - } - - template - bool changed(const char *key, V &v) { - auto &sub = *(SubClassCRTP *)this; - sub(key, v); - return false; - } - void invalidate(const char *) {} - - bool extra() { - return false; - } - template - void extra(const char *, V) {} - - template - RangeParamIgnore range(const char *key, PR ¶m) { - return {}; - } - template - SteppedParamIgnore stepped(const char *key, PS ¶m) { - return {}; - } -}; - -template -struct STFXStorageWriter : public signalsmith::storage::StorageCborWriter { - using signalsmith::storage::StorageCborWriter::StorageCborWriter; - - void info(const char *name, const char *desc) { - sub().extra("name", name); - sub().extra("desc", desc); - } - - template - bool changed(const char *key, V &v) { - sub()(key, v); - return false; - } - void invalidate(const char *) {} - - int version(int v) { - sub()("version", v); - return v; - } - bool extra() { - return false; - } - template - void extra(const char *key, V v) {} - - template - RangeParamIgnore range(const char *key, PR ¶m) { - sub()(key, param); - return {}; - } - template - SteppedParamIgnore stepped(const char *key, PS ¶m) { - sub()(key, param); - return {}; - } - -private: - SubClassCRTP & sub() { - return *(SubClassCRTP *)this; - } -}; - -template -struct STFXStorageReader : public signalsmith::storage::StorageCborReader { - using signalsmith::storage::StorageCborReader::StorageCborReader; - - // This is supplemental - void info(const char *, const char *) {} - - int version(int v) { - sub()("version", v); - return v; - } - bool extra() { - return false; - } - template - void extra(const char *key, V v) {} - - template - bool changed(const char *key, V &v) { - V prev = v; - sub()(key, v); - return v != prev; - } - void invalidate(const char *invalidatedKey) { - //LOG_EXPR(invalidatedKey); - } - - template - RangeParamIgnore range(const char *key, PR ¶m) { - sub()(key, param); - return {}; - } - template - SteppedParamIgnore stepped(const char *key, PS ¶m) { - sub()(key, param); - return {}; - } - -private: - SubClassCRTP & sub() { - return *(SubClassCRTP *)this; - } -}; - -// If void, use itself for CRTP -template<> -struct STFXStorageWriter : public STFXStorageWriter> { - using STFXStorageWriter>::STFXStorageWriter; -}; -template<> -struct STFXStorageReader : public STFXStorageReader> { - using STFXStorageReader>::STFXStorageReader; -}; - -}} // namespace diff --git a/stfx/storage/storage.h b/stfx/storage/storage.h deleted file mode 100644 index 8adc62a..0000000 --- a/stfx/storage/storage.h +++ /dev/null @@ -1,264 +0,0 @@ -#pragma once - -#include "./cbor-walker.h" - -#include -#include - -namespace signalsmith { namespace storage { - -struct StorageDummy { - template - void operator()(const char *, V &) {} -}; - -template -struct StorageScanner { - template - void operator()(const char */*key*/, V &v) { - value(v); - } - -private: - void value(int64_t &v) {}; - void value(uint64_t &v) {}; - void value(int32_t &v) {}; - void value(uint32_t &v) {}; - void value(int16_t &v) {}; - void value(uint16_t &v) {}; - void value(int8_t &v) {}; - void value(uint8_t &v) {}; - void value(float &v) {}; - void value(double &v) {}; - void value(bool &v) {}; - void value(std::string &str) {}; - - template - void value(std::vector &array) { - for (auto &item : array) { - value(item); - } - } - - template - void value(V &v) { - v.state(*(SubClassCRTP *)this); - } -}; - -template -struct StorageCborWriter { - StorageCborWriter(const signalsmith::cbor::CborWriter &writer, std::vector *buffer=nullptr) : cbor(writer) { - if (buffer) buffer->resize(0); - - cbor.openMap(); - } - StorageCborWriter(std::vector &cborBuffer) : StorageCborWriter(signalsmith::cbor::CborWriter(cborBuffer), &cborBuffer) {} - ~StorageCborWriter() { - cbor.close(); - } - - template - void operator()(const char *key, V &value) { - cbor.addUtf8(key); - writeValue(value); - } - -private: - signalsmith::cbor::CborWriter cbor; - -#define STORAGE_BASIC_INT(V) \ - void writeValue(V &value) { \ - cbor.addInt(value); \ - } - STORAGE_BASIC_INT(int64_t) - STORAGE_BASIC_INT(uint64_t) - STORAGE_BASIC_INT(int32_t) - STORAGE_BASIC_INT(uint32_t) - STORAGE_BASIC_INT(int16_t) - STORAGE_BASIC_INT(uint16_t) - STORAGE_BASIC_INT(int8_t) - STORAGE_BASIC_INT(uint8_t) -#undef STORAGE_BASIC_INT - - void writeValue(float &value) { - cbor.addFloat(value); - } - void writeValue(double &value) { - cbor.addFloat(value); - } - void writeValue(bool &value) { - cbor.addBool(value); - } - - // Support writing C strings (but not reading them), so inherited writers can add supplemental hints/info without allocating - void writeValue(const char *cStr) { - cbor.addUtf8(cStr); - } - - void writeValue(std::string &str) { - writeValue(str.c_str()); - } - - template - void writeValue(std::vector &array) { - cbor.openArray(array.size()); - for (auto &item : array) { - writeValue(item); - } - } -#define STORAGE_TYPED_ARRAY(T) \ - void writeValue(std::vector &array) { \ - cbor.addTypedArray(array.data(), array.size()); \ - } - STORAGE_TYPED_ARRAY(uint8_t) - STORAGE_TYPED_ARRAY(int8_t) - STORAGE_TYPED_ARRAY(uint16_t) - STORAGE_TYPED_ARRAY(int16_t) - STORAGE_TYPED_ARRAY(uint32_t) - STORAGE_TYPED_ARRAY(int32_t) - STORAGE_TYPED_ARRAY(uint64_t) - STORAGE_TYPED_ARRAY(int64_t) - STORAGE_TYPED_ARRAY(float) - STORAGE_TYPED_ARRAY(double) -#undef STORAGE_TYPED_ARRAY - - template - void writeValue(Obj &obj) { - cbor.openMap(); - obj.state(*(SubClassCRTP *)this); - cbor.close(); - } -}; - -template -struct StorageCborReader { - using Cbor = signalsmith::cbor::TaggedCborWalker; - - StorageCborReader(Cbor c) : cbor(c) { - if (cbor.isMap()) cbor = cbor.enter(); - } - StorageCborReader(const std::vector &v) : StorageCborReader(Cbor(v)) {} - - template - void operator()(const char *key, V &v) { - if (filterKeyBytes != nullptr) { - if (!keyMatch(key, filterKeyBytes, filterKeyLength)) return; - } - if (!cbor.isUtf8()) return; // We expect a string key - // If we have a filter, we *should* be just in front of the appropriate key, but check anyway - if (!keyMatch(key, (const char *)cbor.bytes(), cbor.length())) { - return; // key doesn't match - } - cbor++; - readValue(v); - } - -private: - Cbor cbor; - const char *filterKeyBytes = nullptr; - size_t filterKeyLength = 0; - - template - void readValue(Obj &obj) { - if (!cbor.isMap()) return; - - cbor = cbor.forEachPair([&](Cbor key, Cbor value){ - if (!key.isUtf8()) return; - - const char *fkb = filterKeyBytes; - size_t fkl = filterKeyLength; - - // Temporarily set key, and scan the object for that property - filterKeyBytes = (const char *)key.bytes(); - filterKeyLength = key.length(); - cbor = key; - obj.state(*(SubClassCRTP *)this); - - filterKeyBytes = fkb; - filterKeyLength = fkl; - }); - } - -#define STORAGE_BASIC_TYPE(V) \ - void readValue(V &v) { \ - v = V(cbor++); \ - } - STORAGE_BASIC_TYPE(int64_t) - STORAGE_BASIC_TYPE(uint64_t) - STORAGE_BASIC_TYPE(int32_t) - STORAGE_BASIC_TYPE(uint32_t) - STORAGE_BASIC_TYPE(int16_t) - STORAGE_BASIC_TYPE(uint16_t) - STORAGE_BASIC_TYPE(int8_t) - STORAGE_BASIC_TYPE(uint8_t) - STORAGE_BASIC_TYPE(float) - STORAGE_BASIC_TYPE(double) - STORAGE_BASIC_TYPE(bool) -#undef STORAGE_BASIC_TYPE - - void readValue(std::string &v) { - if (!cbor.isUtf8()) v.clear(); - v.assign((const char *)cbor.bytes(), cbor.length()); - ++cbor; - } - - template - void readVector(std::vector &array) { - if (!cbor.isArray()) return; - size_t length = 0; - cbor = cbor.forEach([&](Cbor item, size_t index){ - length = index + 1; - if (array.size() < length) array.resize(length); - - cbor = item; - readValue(array[index]); - }); - array.resize(length); - } - - template - void readValue(std::vector &array) { - readVector(array); - } - -#define STORAGE_TYPED_ARRAY(T) \ - void readValue(std::vector &array) { \ - if (cbor.isTypedArray()) { \ - array.resize(cbor.typedArrayLength()); \ - cbor.readTypedArray(array); \ - } else { \ - readVector(array); \ - } \ - } - STORAGE_TYPED_ARRAY(uint8_t) - STORAGE_TYPED_ARRAY(int8_t) - STORAGE_TYPED_ARRAY(uint16_t) - STORAGE_TYPED_ARRAY(int16_t) - STORAGE_TYPED_ARRAY(uint32_t) - STORAGE_TYPED_ARRAY(int32_t) - STORAGE_TYPED_ARRAY(uint64_t) - STORAGE_TYPED_ARRAY(int64_t) - STORAGE_TYPED_ARRAY(float) - STORAGE_TYPED_ARRAY(double) -#undef STORAGE_TYPED_ARRAY - - static bool keyMatch(const char *key, const char *filterKeyBytes, size_t filterKeyLength) { - for (size_t i = 0; i < filterKeyLength; ++i) { - if (key[i] != filterKeyBytes[i]) return false; - } - return key[filterKeyLength] == 0; - } -}; - -// If void, use itself for CRTP -template<> -struct StorageCborWriter : public StorageCborWriter> { - using StorageCborWriter>::StorageCborWriter; -}; -template<> -struct StorageCborReader : public StorageCborReader> { - using StorageCborReader>::StorageCborReader; -}; - -}} // namespace diff --git a/stfx/ui/html/cbor.min.js b/stfx/ui/html/cbor.min.js deleted file mode 100644 index bedce98..0000000 --- a/stfx/ui/html/cbor.min.js +++ /dev/null @@ -1 +0,0 @@ -let CBOR={encode(e,r){let o=r&&r.resizable?(r.resize(0),r):new ArrayBuffer(0,{maxByteLength:64}),f=new DataView(o),i=[o],y=CBOR.encodeTag;function l(e,r,t){var n=1+t;if(o.byteLength+n>o.maxByteLength){let e=1;for(;e<=o.maxByteLength||e<=t;)e*=2;o=new ArrayBuffer(0,{maxByteLength:e}),i.push(o),f=new DataView(o)}var a=o.byteLength;return o.resize(a+n),f.setUint8(a,e<<5|r),a+1}function s(e,r,t){var n;return r<24?l(e,r,t):r<256?(n=l(e,24,t+1),f.setUint8(n,r),n+1):r<65536?(n=l(e,25,t+2),f.setUint16(n,r),n+2):(n=l(e,26,t+4),f.setUint32(n,r),n+4)}if(!function r(t){if("number"==typeof t)Number.isInteger(t)&&t<=Number.MAX_SAFE_INTEGER&&t>=Number.MIN_SAFE_INTEGER?0<=t?t<4294967296?s(0,t,0):t<0x10000000000000000?(e=l(0,27,8),f.setBigUint64(e,BigInt(t))):(e=l(7,27,8),f.setFloat64(e,t)):-4294967296<=t?s(1,-1-t,0):-18446744073709551616n<=t?(e=l(1,27,8),f.setBigUint64(e,BigInt(-1-t))):(e=l(7,26,8),f.setFloat64(e,t)):(e=l(7,27,8),f.setFloat64(e,t));else if("bigint"==typeof t)if(0<=t)if(t<18446744073709551616n){var e=l(0,27,8);f.setBigUint64(e,t)}else{l(6,2,0);for(var n=[];t;)n.unshift(Number(0xffn&t)),t>>=8n;r(new Uint8Array(n).buffer)}else if(-18446744073709551616n<=t){e=l(1,27,8);f.setBigUint64(e,-1n-t)}else{l(6,3,0),t=-1n-t;for(var a=[];t;)a.unshift(Number(0xffn&t)),t>>=8n;r(new Uint8Array(a).buffer)}else if(!1===t)l(7,20,0);else if(!0===t)l(7,21,0);else if(null===t)l(7,22,0);else if(void 0===t)l(7,23,0);else if("string"==typeof t){var i=s(3,(e=(new TextEncoder).encode(t)).length,e.length);new Uint8Array(o).set(e,i)}else if(t instanceof ArrayBuffer)e=s(2,t.byteLength,t.byteLength),new Uint8Array(o).set(new Uint8Array(t),e);else if(t instanceof DataView){i=s(2,t.byteLength,t.byteLength);new Uint8Array(o).set(new Uint8Array(t.buffer,t.byteOffset,t.byteLength),i)}else if(Array.isArray(t))s(4,t.length,0),t.forEach(r);else{if("object"!=typeof t){for(let e=0;e<256;++e)if(t===CBOR.simple[e])return s(7,e,0);throw Error("unknown simple value")}if(y in t)return s(6,(e=t[y](t))[0],0),r(e[1]);l(5,(i=Object.keys(t)).length,0),i.forEach(e=>{r(e),r(t[e])})}}(e),1==i.length)return i[0];let t=0,n=(i.forEach(e=>t+=e.byteLength),new ArrayBuffer(0,{maxByteLength:t})),a=new Uint8Array(n);return i.forEach(e=>{var r=n.byteLength;n.resize(r+e.byteLength),a.set(new Uint8Array(e),r)}),n},decode(s){let o=CBOR.decodeTags,u=0,c=(ArrayBuffer.isView(s)&&(u=s.byteOffset,s=s.buffer),s.byteLength),g=(s=new DataView(s),CBOR.breakCode);function h(e,r,t){switch(e){case 0:return r;case 1:return-1-r;case 2:var n=t?new Uint8Array(s.buffer,u,r):s.buffer.slice(u,u+r);return u+=r,n;case 3:n=new Uint8Array(s.buffer,u,r);return u+=r,(new TextDecoder).decode(n);case 4:var a=[];for(let e=0;e=c)throw Error("end of CBOR");var r=s.getUint8(u++),t=r>>5,n=31&r;switch(n){case 24:return h(t,s.getUint8(u++),e);case 25:return 7==t?(a=s.getFloat16(u),u+=2,a):(a=s.getUint16(u),u+=2,h(t,a,e));case 26:return 7==t?(a=s.getFloat32(u),u+=4,a):(a=s.getUint32(u),u+=4,h(t,a,e));case 27:if(7==t)return a=s.getFloat64(u),u+=8,a;var a=s.getBigUint64(u);if(u+=8,a{t.set(e,n),n+=e.length}),o?t:t.buffer}case 3:{let e="",r=A();for(;r!=g;){if("string"!=typeof r)throw Error("indefinite string with non-string item");e+=r,r=A()}return e}case 4:{var y=[];let e=A();for(;e!=g;)y.push(e),e=A();return y}case 5:{var l=Object.create(null);let e=A();for(;e!=g;)l[e]=A(),e=A();return l}case 7:return g;default:throw Error("invalid indefinite type")}return;default:return h(t,n,e)}}return A()},simple:[],encodeTag:Symbol(),decodeTags:{0:e=>new Date(e),1:e=>new Date(1e3*e),2:r=>{let t=0n;for(let e=0;e{let t=0n;for(let e=0;enew URL(e),258:e=>new Set(e)},encode16(e,r){var t=new Uint8Array(CBOR.encode(e,r));let n="";for(let e=0;e>4).toString(16)+(15&t[e]).toString(16);return n},encode64(e,r){var t=new Uint8Array(CBOR.encode(e,r));let n="";for(let e=0;e{function r(e){return new DataView(e.buffer,e.byteOffset,e.byteLength)}Date.prototype[e]=e=>[1,+e/1e3],URL.prototype[e]=e=>[32,e.href],Set.prototype[e]=e=>[258,Array.from(e)];var t="function"==typeof Float16Array;Uint8Array.prototype[e]=e=>[64,r(e)],Int8Array.prototype[e]=e=>[72,r(e)],Uint16Array.prototype[e]=e=>[y?65:69,r(e)],Int16Array.prototype[e]=e=>[y?73:77,r(e)],Uint32Array.prototype[e]=e=>[y?66:70,r(e)],Int32Array.prototype[e]=e=>[y?74:78,r(e)],BigUint64Array.prototype[e]=e=>[y?67:71,r(e)],BigInt64Array.prototype[e]=e=>[y?75:79,r(e)],t&&(Float16Array.prototype[e]=e=>[y?80:84,r(e)]),Float32Array.prototype[e]=e=>[y?81:85,r(e)],Float64Array.prototype[e]=e=>[y?82:86,r(e)];e=(r,o)=>{const f=r.BYTES_PER_ELEMENT;return e=>{if((e.byteOffset%f||o!=y)&&(e=e.slice()),o!=y){var t=e,n=f;for(let r=0;re,65:e(Uint16Array,!0),66:e(Uint32Array,!0),67:e(BigUint64Array,!0),69:e(Uint16Array,!1),70:e(Uint32Array,!1),71:e(BigUint64Array,!1),72:e=>new Int8Array(e.buffer,e.byteOffset,e.length),73:e(Int16Array,!0),74:e(Int32Array,!0),75:e(BigInt64Array,!0),77:e(Int16Array,!1),78:e(Int32Array,!1),79:e(BigInt64Array,!1),80:t&&e(Float16Array,!0),81:e(Float32Array,!0),82:e(Float64Array,!0),84:t&&e(Float16Array,!1),85:e(Float32Array,!1),86:e(Float64Array,!1)})})(CBOR.encodeTag,!!new Uint8Array(new Uint16Array([256]).buffer)[0]),"object"==typeof module&&module?.exports&&(module.exports=CBOR); diff --git a/stfx/ui/html/generic.html b/stfx/ui/html/generic.html deleted file mode 100644 index a513b0a..0000000 --- a/stfx/ui/html/generic.html +++ /dev/null @@ -1,383 +0,0 @@ - - - - Generic STFX UI - - - -

{name}

-
- -
- - - - - - - - diff --git a/stfx/ui/html/matsui-bundle.min.js b/stfx/ui/html/matsui-bundle.min.js deleted file mode 100644 index 374fbe9..0000000 --- a/stfx/ui/html/matsui-bundle.min.js +++ /dev/null @@ -1,2 +0,0 @@ -"use strict";(e=>{"undefined"==typeof self&&"object"==typeof module?module.exports=e():self.Matsui=e("function"==typeof Matsui?Matsui:self.Matsui)})(e=>{Object.hasOwn||(Object.hasOwn=(e,t)=>Object.prototype.hasOwnProperty.call(e,t));var t=[];let d=e=>e&&"object"==typeof e,i=e=>Array.isArray(e)||ArrayBuffer.isView(e)||e instanceof ArrayBuffer,h=()=>document.createTextNode("");function n(e,t){for(;e.nextSibling&&e.nextSibling!=t;)e.nextSibling.remove()}function a(){var e=document.createDocumentFragment();let t=h(),r=h();return e.append(t,r),{t:e,o:(...e)=>{n(t,r),t.after(...e)}}}let c=Symbol();function f(t){if(d(t)){let e=t[c];for(;e&&e!=t;)e=(t=e)[c]}return t}let o=Symbol(),l=Symbol(),p=Symbol("no change"),s=Symbol("replace"),y={apply(r,n,o){return d(n)?d(r)?i(n)?n:(n[s]&&(o?r[s]=!0:delete n[s]),Object.keys(n).forEach(e=>{var t=n[e];Object.hasOwn(r,e)?null!=t||o?r[e]=y.apply(r[e],t,o):delete r[e]:null==t&&!o||(r[e]=t)}),r):(o&&(n[s]=!0),n):n},make(n,o,e){if(!e||n!==o){if(!d(o))return o;if(!d(n))return o[s]=!0,o;if(i(o))return o;let r={};return Object.keys(o).forEach(e=>{var t;Object.hasOwn(n,e)?void 0!==(t=y.make(n[e],o[e],!0))&&(r[e]=t):r[e]=o[e]}),Object.keys(n).forEach(e=>{Object.hasOwn(o,e)||(r[e]=null)}),e&&0==Object.keys(r).length?void 0:r}},tracked(e,t,r){if(!d(e))return e;let n=t,o=!1,i=p,l=null,a=()=>{var e;clearTimeout(l),i!==p&&(e=i,i=p,n(e))},u=(e,o)=>new Proxy(e,{get(e,t){var r=e[t];return t===c?e:d(r)?u(r,e=>o({[t]:e})):r},set(e,t,r,n){if(null==r)return delete n[t];if((r=f(r))===e[t])return!0;if(n=y.make(e[t],r),d(n))n[s]=!0;else if(r===e[t])return!0;return!!Reflect.set(e,t,r)&&(o({[t]:n}),!0)},deleteProperty(e,t){return!(t in e)||delete e[t]&&(o({[t]:null}),!0)}});return u(e,t=e=>{if(i!==p)i=y.apply(i,e,!0);else if(i=e,r)requestAnimationFrame(a),clearTimeout(l),l=setTimeout(a,0);else if(!o)for(;i!==p;)o=!0,a(),o=!1})},addHidden(e,n){return d(e)?new Proxy(e,{get(e,t){var r;return t===o?n:t===l||t===c?e:(e=e[t],r=d(n)&&t in n,y.addHidden(e,r?n[t]:p))},has(e,t){return t===o||t in e}}):e},getHidden(e,t){var r;return d(e)&&void 0!==(r=f(e[o]))?r===p?t:r:e},withoutHidden(e){return d(e)&&e[l]||e}},u=Symbol(),m=Symbol(),v=Symbol("accessed"),w=Symbol("list-keys"),b={tracked(e,n){if(!d(e))return n[v]=v,e;let o=Array.isArray(e);return new Proxy(e,{get(e,t){var r=e[t];return t===c||t===m?e:t===u?(n[v]=v,e):o&&"length"===t?(n[w]=w,r):("function"!=typeof r||r.prototype||(n[v]=v),t in n||(n[t]={}),b.tracked(r,n[t]))},ownKeys(e){return n[w]=w,Reflect.ownKeys(e)}})},pierce(e,t){return e&&e[t?m:u]||e},accessed:v},g=Symbol(),E=(i,r)=>{for(let e=0;et(r(e))]}Object.freeze(i);let t=!0,l=[],n=null;var e=o=>{o=b.pierce(o);let r=y.withoutHidden(o);var e=f(r);if(t||n!==e)n=d(e)?e:null,t=!1,i.forEach((e,t)=>{t=l[t]={},e(b.tracked(r,t))});else{let n=y.getHidden(o,p);i.forEach((e,t)=>{var r;!function e(t,r){if(r!==p){if(t[v])return 1;if(!d(r)||Array.isArray(r)||r[s])return 1;if(t[w]){for(var n in r)if(e(t[n]||{},r[n]))return 1}else for(var o in r)if(t[o]&&e(t[o],r[o]))return 1}}(l[t],n)||(r=l[t]={},e(b.tracked(o,r,t)))})}};return e[g]=i,e},_=/\$\{/g;function j(n,o){let i=0;for(var l,a=[];l=_.exec(n);){a.push(n.slice(i,l.index));let e=l.index+2,t=e+1,r;for(;te=>e;else{let r=t.split(".");1==r.length?(r=r[0],n[e]=()=>e=>e?.[r]):n[e]=()=>t=>(r.forEach(e=>{t&&"object"==typeof t&&(t=t[e])}),t)}else n[e]=e=>e[t]}return t=>{let e=n.map(e=>"function"==typeof e?e(t):e).filter(e=>""!=e);return 1==e.length?e[0]:e.some(e=>"function"==typeof e)?t=>e.map(e=>"function"==typeof e?e(t):e).join(""):e.join("")}}function A(e){if(/^template$/i.test(e.tagName))return 1;for(var t of e.attributes||[])if("@"==t.name[0])return 1}function S(e){return/^script$/i.test(e.tagName)}function $(e){return e.slice(1).toLowerCase().replace(/-+(.)/g,(e,t)=>t.toUpperCase())}let L=/(\{[a-z0-9_=\.-]+\}|\uF74A[0-9]+\uF74B)/giu,T=/((\$[a-z0-9_-]+)*)(\{([a-z0-9_=\.-]+)\}|\uF74A([0-9]+)\uF74B)/giu,k=Symbol();function M(e){let f=e.content||e,s=[],d={},p=!1,m=(Array.from(f.childNodes).forEach(t=>{if("TEMPLATE"==t.tagName){var r=t.getAttribute("name");if(r){p=!0;let e=e=>null;t.hasAttribute("$filter")&&(e=O(t.getAttribute("$filter"))),d[r]={i:M(t),l:t.getAttribute("@scoped"),u:t[k],p:e},t.remove()}}}),function r(t,a){if(3==t.nodeType){var u=a,o=t.nodeValue;let e,n=0;for(;e=T.exec(o);){let i=o.slice(n,e.index),l=(n=T.lastIndex,e[1].split("$").slice(1)),t=e[4],r=e[5],a=null;if(t){let e="="==t?[]:t.split(".");a=t=>(e.forEach(e=>{t&&"object"==typeof t&&(t=t[e])}),t)}s.push((n,e)=>{let o=a||e[r];if("function"==typeof o)if("template"===l[0])(n=n.extend()).add("template",o),o=e=>e;else if("scoped"===l[0]){n=n.extend();let t=o;n.add("scoped",x(e=>t(e,n))),o=e=>e}return{m:u,v:(e,t,r)=>{i&&e.before(i),r=function(n,e,o){let i=e.map(e=>{var t=n.named[e];if(t)return t;let r="Template not found: "+e;return console.error(r),e=>({node:document.createTextNode(r),updates:[]})});return function t(r){if(r>=e.length)return o;let n=i[r];return e=>n(t(r+1))}(0)(n.dynamic)}(n,l,r),e.before(r.node),"function"==typeof o?t.push(E(r.updates,o)):r.updates.forEach(e=>e(o))}}})}if(0e.nodeValue=t:e=>e.remove();s.push(e=>({m:u,v:r}))}}else if(1===t.nodeType){if(A(t)&&a.length){if("TEMPLATE"==t.tagName&&t.hasAttribute("name"))throw Error('