2 Commits

Author SHA1 Message Date
  Ludovic 'Archivist' Lagouardette 5b81cba7c8 Added some cryptographic bases and made testing function preconditions easier 9 months ago
  Ludovic 'Archivist' Lagouardette 1a9878ece9 Changed type for arrays from vector to deque 9 months ago
9 changed files with 572 additions and 127 deletions
Split View
  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 View File

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

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

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

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

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

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

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

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

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

Loading…
Cancel
Save