2 Commits

Auteur SHA1 Bericht Datum
  Ludovic 'Archivist' Lagouardette 5b81cba7c8 Added some cryptographic bases and made testing function preconditions easier 10 maanden geleden
  Ludovic 'Archivist' Lagouardette 1a9878ece9 Changed type for arrays from vector to deque 10 maanden geleden
9 gewijzigde bestanden met toevoegingen van 572 en 127 verwijderingen
  1. +1
    -1
      CMakeLists.txt
  2. +4
    -2
      include/UserScript.h
  3. +147
    -0
      include/UserScriptRequire.h
  4. +12
    -0
      include/UserScriptWizardry.h
  5. +10
    -2
      script_exe/main.cpp
  6. +4
    -2
      src/generator.cpp
  7. +3
    -13
      src/interpreter.cpp
  8. +103
    -107
      src/std/array.cpp
  9. +288
    -0
      src/std/crypto.cpp

+ 1
- 1
CMakeLists.txt Bestand weergeven

@ -25,7 +25,7 @@ add_library(UserScript STATIC
src/generator.cpp
src/interpreter.cpp
src/lex.cpp
src/parse.cpp src/std/array.cpp)
src/parse.cpp src/std/array.cpp src/std/crypto.cpp include/UserScriptRequire.h include/UserScriptWizardry.h)
target_include_directories(UserScript PUBLIC include)
include_directories(priv_include)

+ 4
- 2
include/UserScript.h Bestand weergeven

@ -2,6 +2,7 @@
#include <memory>
#include <string>
#include <variant>
#include <deque>
#include <vector>
#include <optional>
@ -26,8 +27,8 @@ namespace scripting {
};
struct array {
std::vector<script_value> value;
operator std::vector<script_value>&() {
std::deque<script_value> value;
operator std::deque<script_value>&() {
return value;
}
};
@ -57,4 +58,5 @@ namespace scripting {
std::unique_ptr<UserScript> prepare_interpreter(const std::string& code);
std::unique_ptr<UserScript> register_array_lib(std::unique_ptr<UserScript> target, bool recursive_arrays = false, int32_t size_limit = 1024);
std::unique_ptr<UserScript> register_crypto_lib(std::unique_ptr<UserScript> target, int32_t array_size_limit = 1024, int32_t string_size_limit = 1024);
}

+ 147
- 0
include/UserScriptRequire.h Bestand weergeven

@ -0,0 +1,147 @@
#pragma once
#include "UserScript.h"
#include "UserScriptWizardry.h"
#include <concepts>
#include <span>
#include <ranges>
/*template<size_t arg_number>
struct verifier_base {
static constexpr size_t argument_id = arg_number;
};*/
namespace details {
template<size_t argument_counter, typename T>
struct TypeVerifier {
static constexpr size_t argument_id = argument_counter;
bool verify(scripting::UserScript* self, std::vector<scripting::argument>& args) {
if(argument_id >= args.size()) return false;
if(args.size() -1 -argument_id < 0) return false;
auto& argument = args[args.size() -1 -argument_id];
std::optional<std::reference_wrapper<scripting::script_value>> v = std::visit(
wizardry::overloaded{
[&](scripting::script_variable& v) -> std::optional<std::reference_wrapper<scripting::script_value>> {return self->getValue(v.name);},
[&](scripting::script_value& v) -> std::optional<std::reference_wrapper<scripting::script_value>> {return v;},
},
argument
);
if(not v) return false;
return std::visit(wizardry::overloaded{
[](T& ) {return true;},
[](auto&) {return false;}
}, v.value().get());
}
};
template<size_t argument_counter>
struct VariableVerifier {
static constexpr size_t argument_id = argument_counter;
bool verify(scripting::UserScript* self, std::vector<scripting::argument>& args) {
if(argument_id >= args.size()) return false;
if(args.size() -1 -argument_id < 0) return false;
auto& argument = args[args.size() -1 -argument_id];
return std::visit(
wizardry::overloaded{
[&](scripting::script_variable& v) -> bool {return self->getValue(v.name).has_value();},
[&](scripting::script_value& v) {return false;},
},
argument
);
}
};
template<size_t skip = 0>
struct NoArrays {
bool verify(scripting::UserScript* self, std::vector<scripting::argument> args) {
for(auto& elem : std::ranges::reverse_view(args) | std::ranges::views::drop(skip)) {
std::optional<std::reference_wrapper<scripting::script_value>> v;
std::visit(
wizardry::overloaded{
[&](scripting::script_variable& n) {v = self->getValue(n.name);},
[&](scripting::script_value& n) {v = n;},
},
elem
);
if(not v) return false;
if(std::visit(wizardry::overloaded{
[](scripting::array&) {return true;},
[](auto&) {return false;}
}, v.value().get())) {
return false;
}
}
return true;
}
};
template<size_t sz = 0>
struct SizeEquals {
bool verify(scripting::UserScript*, std::vector<scripting::argument> args) {
return args.size() == sz;
}
};
template<size_t sz = 0>
struct SizeAtLeast {
bool verify(scripting::UserScript*, std::vector<scripting::argument> args) {
return args.size() >= sz;
}
};
template<size_t argument_counter>
struct OctetArrayVerifier {
static constexpr size_t argument_id = argument_counter;
bool verify(scripting::UserScript* self, std::vector<scripting::argument>& args) {
if(argument_id >= args.size()) return false;
if(args.size() -1 -argument_id < 0) return false;
auto& argument = args[args.size() -1 -argument_id];
std::optional<std::reference_wrapper<scripting::script_value>> v;
std::visit(
wizardry::overloaded{
[&](scripting::script_variable& elem) {v = self->getValue(elem.name);},
[&](scripting::script_value& elem) {v = elem;}
},
argument
);
if(not v) return false;
return std::visit(
wizardry::overloaded{
[](scripting::array& ary) -> bool {
for(auto& elem : ary.value) {
if(std::holds_alternative<int32_t>(elem)) {
if(auto& byte = std::get<int32_t>(elem); byte < 0 || byte > 255) {
return false;
}
} else {
return false;
}
}
return true;
},
[] (auto&) -> bool {return false;}
},
v.value().get()
);
}
};
}
template<typename... verifiers>
struct Verify;
template<typename n, typename... verifiers>
struct Verify<n, verifiers...> {
bool verify(scripting::UserScript* self, std::vector<scripting::argument>& args) {
return n{}.verify(self, args) && Verify<verifiers...>{}.verify(self, args);
}
};
template<>
struct Verify<> {
bool verify(scripting::UserScript*, const std::vector<scripting::argument>&) {
return true;
}
};

+ 12
- 0
include/UserScriptWizardry.h Bestand weergeven

@ -0,0 +1,12 @@
#pragma once
namespace wizardry {
// taken from cppreference: https://en.cppreference.com/w/cpp/utility/variant/visit
template<class... Ts>
struct overloaded : Ts ... {
using Ts::operator()...;
};
// explicit deduction guide (not needed as of C++20)
template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
}

+ 10
- 2
script_exe/main.cpp Bestand weergeven

@ -256,7 +256,11 @@ void immediate_interactive() {
engine->registerFunction("exit", std::make_unique<fn_exit>(should_exit));
engine->registerFunction("set", std::make_unique<set>());
engine = scripting::register_array_lib(std::move(engine), true, 4096);
constexpr size_t array_limit = 4096;
constexpr size_t string_limit = 4096;
engine = scripting::register_array_lib(std::move(engine), true, array_limit);
engine = scripting::register_crypto_lib(std::move(engine), array_limit, string_limit);
engine->registerFunction("print", std::make_unique<print>(std::cout));
while (not should_exit) {
@ -288,7 +292,11 @@ void exec(std::span args) {
engine->registerFunction("terminate", std::make_unique<terminate>());
engine->registerFunction("set", std::make_unique<set>());
engine = scripting::register_array_lib(std::move(engine), true, 4096);
constexpr size_t array_limit = 4096;
constexpr size_t string_limit = 4096;
engine = scripting::register_array_lib(std::move(engine), true, array_limit);
engine = scripting::register_crypto_lib(std::move(engine), array_limit, string_limit);
engine->registerFunction("print", std::make_unique<print>(std::cout));
bool exit = false;

+ 4
- 2
src/generator.cpp Bestand weergeven

@ -1,3 +1,5 @@
#include <ranges>
#include "UserScript/interpreter.h"
namespace scripting {
@ -97,8 +99,8 @@ namespace scripting {
void
handle<ast::command_expression>(std::vector<ByteCodeInterpreter::operand> &ctx, std::vector<script_error> &errors,
ast::command_expression &cmd) {
for (auto it = cmd.arguments.rbegin(); it != cmd.arguments.rend(); ++it) {
std::visit([&](auto &v) { handle(ctx, errors, *v); }, p">(*it)->contents);
for (auto& argument : std::ranges::reverse_view(cmd.arguments)) {
std::visit([&](auto &v) { handle(ctx, errors, *v); }, n">argument->contents);
}
ctx.push_back(
ByteCodeInterpreter::operand{.element = ByteCodeInterpreter::function_tag{.name = cmd.name.value, .arity = cmd.arguments.size()}, .location = cmd.location});

+ 3
- 13
src/interpreter.cpp Bestand weergeven

@ -1,4 +1,5 @@
#include "UserScript/interpreter.h"
#include "UserScriptWizardry.h"
namespace scripting {
void to_null(script_value& value, auto on_value, auto on_error) {
@ -30,17 +31,6 @@ namespace scripting {
}
}
namespace wizardry {
// taken from cppreference: https://en.cppreference.com/w/cpp/utility/variant/visit
template<class... Ts>
struct overloaded : Ts ... {
using Ts::operator()...;
};
// explicit deduction guide (not needed as of C++20)
template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
}
bool ByteCodeInterpreter::step(std::optional<script_error>& error) {
if(instruction_ptr >= bytecode.size()) return true;
@ -49,11 +39,11 @@ namespace scripting {
bool ret = std::visit(wizardry::overloaded{
[&](script_value& v){
execution_stack.push_back(v);
execution_stack.emplace_back(v);
return false;
},
[&](variable_tag& v){
execution_stack.push_back(script_variable{.name = v.name});
execution_stack.emplace_back(script_variable{.name = v.name});
return false;
},
[&](ByteCodeInterpreter::operator_t& v){

+ 103
- 107
src/std/array.cpp Bestand weergeven

@ -1,8 +1,8 @@
#include "UserScript.h"
#include "UserScriptRequire.h"
#include <algorithm>
#include <span>
#include <utility>
#include <utility>
using interpreter = decltype(scripting::prepare_interpreter({}));
using namespace scripting;
@ -18,28 +18,16 @@ struct fn_array final : public scripting::function_impl {
std::optional<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& error) final {
array ary;
if(not can_contain_arrays) {
if(std::any_of(n.begin(), n.end(), [&](argument& arg) {
if(std::holds_alternative<scripting::script_value>(arg)) {
return true;
} else {
const auto& val = self->resolve(std::get<scripting::script_variable>(arg).name);
if(not can_contain_arrays && std::holds_alternative<array>(val)) {
return true;
}
}
return false;
})) {
error = script_error{
.message = "/array: arrays cannot contain other arrays"
};
return std::nullopt;
}
if(not can_contain_arrays && details::NoArrays<0>{}.verify(self, n)) {
error = script_error{
.message = "/array: arrays cannot contain other arrays"
};
return std::nullopt;
}
if(n.size() > std::max<int32_t>(0, size_limit)) {
error = script_error{
.message = "/array: arrays cannot contain other arrays"
.message = "/array: arrays exceeds max size"
};
return std::nullopt;
}
@ -62,7 +50,9 @@ struct fn_array_reverse final : public scripting::function_impl {
fn_array_reverse() = default;
std::optional<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& error) final {
if(n.size() != 1) {
using verifier = Verify<details::TypeVerifier<0, array>>;
if(verifier{}.verify(self, n)) {
error = script_error{
.message = "/array_reverse takes exactly 1 argument of type array"
};
@ -78,13 +68,6 @@ struct fn_array_reverse final : public scripting::function_impl {
target = self->resolve(std::get<scripting::script_variable>(arg).name);
}
if(not std::holds_alternative<array>(target)) {
error = script_error{
.message = "/array_reverse takes exactly 1 argument of type array"
};
return std::nullopt;
}
auto& ary = std::get<array>(target);
std::reverse(ary.value.begin(), ary.value.end());
@ -101,20 +84,19 @@ struct fn_array_append final : public scripting::function_impl {
const bool can_contain_arrays;
fn_array_append(std::string _name, int32_t _size_limit, bool _can_contain_arrays)
: name(std::move(_name))
, size_limit(_size_limit)
, can_contain_arrays(_can_contain_arrays)
: name(std::move(_name))
, size_limit(_size_limit)
, can_contain_arrays(_can_contain_arrays)
{}
std::optional<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& error) final {
if(n.size() < 1) {
error = script_error{
.message = "/array_append: must provide an array variable as first argument"
};
return std::nullopt;
}
using verifier = Verify<
details::TypeVerifier<0, array>,
details::VariableVerifier<0>,
details::SizeAtLeast<1>
>;
if(std::holds_alternative<script_value>(n.front())) {
if(verifier{}.verify(self, n) && can_contain_arrays || details::NoArrays<1>{}.verify(self, n)) {
error = script_error{
.message = "/array_append: must provide an array variable as first argument"
};
@ -124,41 +106,8 @@ struct fn_array_append final : public scripting::function_impl {
auto target = self->getValue(std::get<script_variable>(n.back()).name);
n.pop_back();
if(not target) {
error = script_error{
.message = "/array_append: provided variable name is undefined"
};
return std::nullopt;
}
if(not std::holds_alternative<array>(target.value().get())) {
error = script_error{
.message = "/array_append: provided variable is not an array"
};
return std::nullopt;
}
auto& ary = std::get<array>(target.value().get()).value;
if(not can_contain_arrays) {
if(std::any_of(n.begin(), n.end(), [&](argument& arg) {
if(std::holds_alternative<scripting::script_value>(arg)) {
return true;
} else {
const auto& val = self->resolve(std::get<scripting::script_variable>(arg).name);
if(not can_contain_arrays && std::holds_alternative<array>(val)) {
return true;
}
}
return false;
})) {
error = script_error{
.message = "/array_append: arrays cannot contain other arrays"
};
return std::nullopt;
}
}
if(n.size() + ary.size() > std::max<int32_t>(0, size_limit)) {
error = script_error{
.message = "/array_append: array would exceed size limit"
@ -180,13 +129,70 @@ struct fn_array_append final : public scripting::function_impl {
~fn_array_append() final = default;
};
struct fn_array_prepend final : public scripting::function_impl {
const std::string name;
const int32_t size_limit;
const bool can_contain_arrays;
fn_array_prepend(std::string _name, int32_t _size_limit, bool _can_contain_arrays)
: name(std::move(_name))
, size_limit(_size_limit)
, can_contain_arrays(_can_contain_arrays)
{}
std::optional<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& error) final {
using verifier = Verify<
details::TypeVerifier<0, array>,
details::VariableVerifier<0>,
details::SizeEquals<2>
>;
if((verifier{}.verify(self, n)) && (can_contain_arrays || details::NoArrays<1>{}.verify(self, n))) {
error = script_error{
.message = "/array_append: must provide an array variable as first argument"
};
return std::nullopt;
}
auto target = self->getValue(std::get<script_variable>(n.back()).name);
n.pop_back();
auto& ary = std::get<array>(target.value().get()).value;
if(n.size() + ary.size() > std::max<int32_t>(0, size_limit)) {
error = script_error{
.message = name + ": array would exceed size limit"
};
return script_value{0};
}
if(std::holds_alternative<scripting::script_value>(n.front())) {
ary.push_front(std::get<scripting::script_value>(n.front()));
} else {
ary.push_front(self->resolve(std::get<scripting::script_variable>(n.front()).name));
}
return script_value{1};
}
~fn_array_prepend() final = default;
};
struct fn_array_pop final : public scripting::function_impl {
fn_array_pop() = default;
std::string name;
fn_array_pop(std::string _name) : name(std::move(_name)) {}
std::optional<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& error) final {
if(n.size() != 1) {
using verifier = Verify<
details::TypeVerifier<0, array>,
details::VariableVerifier<0>,
details::SizeEquals<1>
>;
if(verifier{}.verify(self, n)) {
error = script_error{
.message = "/array_pop takes exactly 1 argument of type array"
.message = n">name + " takes exactly 1 argument of type array"
};
return std::nullopt;
}
@ -200,13 +206,6 @@ struct fn_array_pop final : public scripting::function_impl {
target = self->resolve(std::get<scripting::script_variable>(arg).name);
}
if(not std::holds_alternative<array>(target)) {
error = script_error{
.message = "/array_pop takes exactly 1 argument of type array"
};
return std::nullopt;
}
auto& ary = std::get<array>(target);
if(ary.value.empty()) {
@ -226,8 +225,13 @@ struct fn_array_pop final : public scripting::function_impl {
struct fn_array_size final : public scripting::function_impl {
fn_array_size() = default;
std::optional<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& error) final {
if(n.size() != 1) {
std::optional<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& error) final {using verifier = Verify<
details::TypeVerifier<0, array>,
details::VariableVerifier<0>,
details::SizeEquals<1>
>;
if(verifier{}.verify(self, n)) {
error = script_error{
.message = "/array_size takes exactly 1 argument of type array"
};
@ -243,13 +247,6 @@ struct fn_array_size final : public scripting::function_impl {
target = self->resolve(std::get<scripting::script_variable>(arg).name);
}
if(not std::holds_alternative<array>(target)) {
error = script_error{
.message = "/array_size takes exactly 1 argument of type array"
};
return std::nullopt;
}
return static_cast<int32_t>(std::get<array>(target).value.size());
}
@ -262,7 +259,20 @@ struct fn_array_index final : public scripting::function_impl {
std::optional<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& error) final {
if(n.size() != 2) {
error = script_error{
.message = "/array_index takes exactly 1 argument of type array followed by an integer, provided a different amount"
.message = "/array_size takes exactly 2 argument of type (array, integer)"
};
return std::nullopt;
}
using verifier = Verify<
details::TypeVerifier<0, array>,
details::VariableVerifier<0>,
details::TypeVerifier<1, int32_t>
>;
if(verifier{}.verify(self, n)) {
error = script_error{
.message = "/array_index takes exactly 2 argument of type (array, integer)"
};
return std::nullopt;
}
@ -276,13 +286,6 @@ struct fn_array_index final : public scripting::function_impl {
target = self->resolve(std::get<scripting::script_variable>(arg).name);
}
if(not std::holds_alternative<array>(target)) {
error = script_error{
.message = "/array_index takes exactly 1 argument of type array followed by an integer, argument 1 is not an array"
};
return std::nullopt;
}
auto& idx_arg = n.front();
script_value idx;
@ -292,13 +295,6 @@ struct fn_array_index final : public scripting::function_impl {
idx = self->resolve(std::get<scripting::script_variable>(idx_arg).name);
}
if(not std::holds_alternative<int32_t>(idx)) {
error = script_error{
.message = "/array_index takes exactly 1 argument of type array followed by an integer, argument 2 is not an integer"
};
return std::nullopt;
}
if(static_cast<int32_t>(std::get<array>(target).value.size()) <= std::get<int32_t>(idx)) {
error = script_error{
.message = "/array_index index must be smaller that the array size, the first element of the array has index 0"
@ -406,17 +402,17 @@ struct fn_array_set final : public scripting::function_impl {
};
namespace scripting {
interpreter register_array_lib(interpreter target, bool recursive_arrays, int32_t size_limit) {
target->registerFunction("array", std::make_unique<fn_array>(size_limit, recursive_arrays));
target->registerFunction("array_reverse", std::make_unique<fn_array_reverse>());
target->registerFunction("array_append", std::make_unique<fn_array_append>("array_append", size_limit, recursive_arrays));
target->registerFunction("array_push", std::make_unique<fn_array_append>("array_push", size_limit, recursive_arrays));
target->registerFunction("array_pop", std::make_unique<fn_array_pop>());
target->registerFunction("array_pop", std::make_unique<fn_array_pop>(sa">"array_pop"));
target->registerFunction("array_size", std::make_unique<fn_array_size>());
target->registerFunction("array_index", std::make_unique<fn_array_index>());
target->registerFunction("array_set", std::make_unique<fn_array_set>());
target->registerFunction("queue_enqueue", std::make_unique<fn_array_prepend>("queue_enqueue", size_limit, recursive_arrays));
target->registerFunction("queue_dequeue", std::make_unique<fn_array_pop>("queue_dequeue"));
return std::move(target);
}
}

+ 288
- 0
src/std/crypto.cpp Bestand weergeven

@ -0,0 +1,288 @@
#include "UserScript.h"
#include <algorithm>
#include <span>
#include <utility>
#include "UserScriptWizardry.h"
#include "UserScriptRequire.h"
using interpreter = decltype(scripting::prepare_interpreter({}));
using namespace scripting;
namespace bad_cryptography {
struct encode_n_parameters {
uint8_t spin;
uint8_t clash;
uint8_t maim;
};
static constexpr uint8_t encode_n_1(uint8_t value, const encode_n_parameters &params) {
value += 42;
value ^= 0b01010101;
value += params.clash;
value = std::rotl(value, params.spin);
value ^= 0b10101010;
value ^= params.maim;
return value;
}
static constexpr uint8_t decode_n_1(uint8_t value, const encode_n_parameters &params) {
value ^= params.maim;
value ^= 0b10101010;
value = std::rotl(value, -params.spin);
value -= params.clash;
value ^= 0b01010101;
value -= 42;
return value;
}
namespace eliminated_001 {
constexpr encode_n_parameters params{.spin = 3, .clash = 17, .maim = 54};
static_assert(0 == decode_n_1(encode_n_1(0, params), params));
static_assert(10 == decode_n_1(encode_n_1(10, params), params));
static_assert(100 == decode_n_1(encode_n_1(100, params), params));
static_assert(20 == decode_n_1(encode_n_1(20, params), params));
static_assert(200 == decode_n_1(encode_n_1(200, params), params));
static_assert(117 == decode_n_1(encode_n_1(117, params), params));
static_assert(42 == decode_n_1(encode_n_1(42, params), params));
};
namespace eliminated_002 {
constexpr encode_n_parameters params{.spin = 7, .clash = 97, .maim = 154};
static_assert(0 == decode_n_1(encode_n_1(0, params), params));
static_assert(10 == decode_n_1(encode_n_1(10, params), params));
static_assert(100 == decode_n_1(encode_n_1(100, params), params));
static_assert(20 == decode_n_1(encode_n_1(20, params), params));
static_assert(200 == decode_n_1(encode_n_1(200, params), params));
static_assert(117 == decode_n_1(encode_n_1(117, params), params));
static_assert(42 == decode_n_1(encode_n_1(42, params), params));
};
}
namespace scripting {
struct fn_string_to_binary final : public scripting::function_impl {
int32_t array_size_limit;
explicit fn_string_to_binary(int32_t _array_size_limit)
: array_size_limit(_array_size_limit)
{}
std::optional<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& error) final {
if(n.size() != 1) {
error = script_error{
.message = "/string_to_binary takes exactly 1 argument of type string"
};
return std::nullopt;
}
auto& arg = n.front();
script_value target;
if(std::holds_alternative<scripting::script_value>(arg)) {
target = std::get<scripting::script_value>(arg);
} else {
target = self->resolve(std::get<scripting::script_variable>(arg).name);
}
if(not std::holds_alternative<std::string>(target)) {
error = script_error{
.message = "/string_to_binary takes exactly 1 argument of type string"
};
return std::nullopt;
}
auto& str = std::get<std::string>(target);
array result;
std::transform(str.begin(), str.end(), std::back_inserter(result.value), [&](char value) -> script_value {
return (int32_t)value;
});
return result;
}
~fn_string_to_binary() final = default;
};
struct fn_binary_to_string final : public scripting::function_impl {
int32_t string_size_limit;
explicit fn_binary_to_string(int32_t _string_size_limit)
: string_size_limit(_string_size_limit) {}
std::optional<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& error) final {
if(n.size() != 1) {
error = script_error{
.message = "/binary_to_string takes exactly 1 argument of type array"
};
return std::nullopt;
}
auto& arg = n.front();
script_value target;
if(std::holds_alternative<scripting::script_value>(arg)) {
target = std::get<scripting::script_value>(arg);
} else {
target = self->resolve(std::get<scripting::script_variable>(arg).name);
}
if(not std::holds_alternative<array>(target)) {
error = script_error{
.message = "/binary_to_string takes exactly 1 argument of type array"
};
return std::nullopt;
}
auto& ary = std::get<array>(target);
if(ary.value.size() > string_size_limit) {
error = script_error{
.message = "/binary_to_string: array is too bit to fit string type"
};
return std::nullopt;
}
std::string result;
const bool valid = std::any_of(ary.value.begin(), ary.value.end(), [](auto val) {
return std::visit(
wizardry::overloaded{
[](int32_t v) { return 0 <= v && v <= 255; },
[](auto v) { return false; }
},
val
);
});
if(not valid) {
error = script_error{
.message = "/binary_to_string takes exactly 1 argument of type array of which contents must be numbers between 0 and 255 included"
};
return std::nullopt;
}
constexpr auto byte_convert = [](int32_t v) -> char {
const uint32_t v2 = v;
const uint8_t v3 = v2 & 0b1111'1111;
return char(v3);
};
std::transform(ary.value.begin(), ary.value.end(), std::back_inserter(result), [&](const script_value& value) -> char {
return std::visit(
wizardry::overloaded{
byte_convert,
[](auto v) { return '\0'; }
},
value
);
});
return result;
}
~fn_binary_to_string() final = default;
};
struct fn_encode_n final : public scripting::function_impl {
int32_t string_size_limit;
explicit fn_encode_n(int32_t _string_size_limit)
: string_size_limit(_string_size_limit) {}
std::optional<script_value> apply(UserScript* self, std::vector<argument> n, std::optional<script_error>& error) final {
using verifier = Verify<
details::SizeEquals<4>,
details::TypeVerifier<0, int32_t>,
details::TypeVerifier<1, int32_t>,
details::TypeVerifier<2, int32_t>,
details::OctetArrayVerifier<3>
>;
if(not verifier{}.verify(self, n)) {
error = script_error{
.message = "/encode_n takes exactly 3 arguments of type integer and an argument of type array"
};
return std::nullopt;
}
auto& arg = n.front();
script_value target;
if(std::holds_alternative<scripting::script_value>(arg)) {
target = std::get<scripting::script_value>(arg);
} else {
target = self->resolve(std::get<scripting::script_variable>(arg).name);
}
bad_cryptography::encode_n_parameters params;
{
auto& arg = n[3];
script_value target;
if (std::holds_alternative<scripting::script_value>(arg)) {
target = std::get<scripting::script_value>(arg);
} else {
target = self->resolve(std::get<scripting::script_variable>(arg).name);
}
params.spin = std::get<int32_t>(target);
}
{
auto& arg = n[2];
script_value target;
if (std::holds_alternative<scripting::script_value>(arg)) {
target = std::get<scripting::script_value>(arg);
} else {
target = self->resolve(std::get<scripting::script_variable>(arg).name);
}
params.clash = std::get<int32_t>(target);
}
{
auto& arg = n[1];
script_value target;
if (std::holds_alternative<scripting::script_value>(arg)) {
target = std::get<scripting::script_value>(arg);
} else {
target = self->resolve(std::get<scripting::script_variable>(arg).name);
}
params.maim = std::get<int32_t>(target);
}
auto ary = std::get<array>(target);
if(ary.value.size() > string_size_limit) {
error = script_error{
.message = "/encode_n: array is too bit to fit string type"
};
return std::nullopt;
}
std::transform(ary.value.begin(), ary.value.end(), ary.value.begin(), [&](const script_value& value) {
return std::visit(
wizardry::overloaded{
[&](int32_t v) -> script_value { return script_value{bad_cryptography::encode_n_1(v, params)};},
[](auto v) -> script_value { return null{}; }
},
value
);
});
return ary;
}
~fn_encode_n() final = default;
};
interpreter register_crypto_lib(interpreter target, int32_t array_size_limit, int32_t string_size_limit) {
target->registerFunction("string_to_binary", std::make_unique<fn_string_to_binary>(array_size_limit));
target->registerFunction("binary_to_string", std::make_unique<fn_binary_to_string>(string_size_limit));
target->registerFunction("encode_n", std::make_unique<fn_encode_n>(string_size_limit));
return std::move(target);
}
}

Laden…
Annuleren
Opslaan