/
redgpu
/
tech5
Обзор
Документация
Войти
/
redgpu
/
tech5
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
source/shared/idlib/runtimeexpression.h
380 строк
13 KB
Justin Marshall
First pass idLib conversion from hex rays4.
08 авг 2026, 23:54
08 авг 2026, 23:54
09a4cb9
Код
Авторство
О чём код?
#pragma once #include "text/str.h" #include <algorithm> #include <cctype> #include <cstdint> #include <cstdio> #include <cstdlib> #include <cstring> #include <new> template<typename varType, typename varContextType> class idRuntimeExpression { public: enum { MAX_NODES = 253, INVALID_INDEX = 255 }; class ExpNode { public: enum op_t { OP_NONE = 0, OP_VAL, OP_VAR, OP_MUL, OP_DIV, OP_MAX, OP_MIN, OP_LERP, OP_CLAMP, OP_SUB, OP_ADD }; ExpNode() : coef(1.0f), op(OP_NONE), parent(INVALID_INDEX), var0(INVALID_INDEX), var1(INVALID_INDEX), var2(INVALID_INDEX), varId() { } float coef; op_t op; std::uint8_t parent; std::uint8_t var0; std::uint8_t var1; std::uint8_t var2; varType varId; }; idRuntimeExpression() : root(INVALID_INDEX), nodes(nullptr), numNodes(0), capacity(0), granularity(5), memTag(5), listStatic(0) { } ~idRuntimeExpression() { Clear(); } idRuntimeExpression(const idRuntimeExpression&) = delete; idRuntimeExpression& operator=(const idRuntimeExpression&) = delete; bool Parse(const char* expression, varContextType& context) { Clear(); const char* cursor = expression == nullptr ? "" : expression; const int parsedRoot = ParseAddSub(cursor, context); SkipWhitespace(cursor); if (parsedRoot == INVALID_INDEX || *cursor != '\0') { Clear(); return false; } root = parsedRoot; return true; } float Eval(varContextType& context) const { return root == INVALID_INDEX ? 0.0f : EvalNode(root, context); } idStr PrintExp() const { return root == INVALID_INDEX ? idStr() : PrintNode(root); } int NumNodes() const { return numNodes; } void Clear() { if (nodes != nullptr) { for (int index = 0; index < capacity; ++index) { nodes[index].~ExpNode(); } std::free(nodes); } root = INVALID_INDEX; nodes = nullptr; numNodes = 0; capacity = 0; } private: int root; ExpNode* nodes; int numNodes; int capacity; std::int16_t granularity; std::uint8_t memTag; std::uint8_t listStatic; static void SkipWhitespace(const char*& cursor) { while (std::isspace(static_cast<unsigned char>(*cursor)) != 0) { ++cursor; } } bool Grow() { if (capacity >= MAX_NODES) { return false; } const int newCapacity = std::min(static_cast<int>(MAX_NODES), capacity + (granularity > 0 ? granularity : 5)); ExpNode* const replacement = static_cast<ExpNode*>( std::malloc(sizeof(ExpNode) * static_cast<std::size_t>(newCapacity)) ); if (replacement == nullptr) { return false; } for (int index = 0; index < newCapacity; ++index) { new (&replacement[index]) ExpNode(); } for (int index = 0; index < numNodes; ++index) { replacement[index] = nodes[index]; } if (nodes != nullptr) { for (int index = 0; index < capacity; ++index) { nodes[index].~ExpNode(); } std::free(nodes); } nodes = replacement; capacity = newCapacity; return true; } int AddNode(const typename ExpNode::op_t op, const int child0 = INVALID_INDEX, const int child1 = INVALID_INDEX, const int child2 = INVALID_INDEX, const float coefficient = 1.0f, const varType* variable = nullptr) { if (numNodes == capacity && !Grow()) { return INVALID_INDEX; } const int index = numNodes++; ExpNode& node = nodes[index]; node.op = op; node.coef = coefficient; node.var0 = static_cast<std::uint8_t>(child0); node.var1 = static_cast<std::uint8_t>(child1); node.var2 = static_cast<std::uint8_t>(child2); if (variable != nullptr) { node.varId = *variable; } if (child0 != INVALID_INDEX) nodes[child0].parent = static_cast<std::uint8_t>(index); if (child1 != INVALID_INDEX) nodes[child1].parent = static_cast<std::uint8_t>(index); if (child2 != INVALID_INDEX) nodes[child2].parent = static_cast<std::uint8_t>(index); return index; } int ParseAddSub(const char*& cursor, varContextType& context) { int left = ParseMulDiv(cursor, context); while (left != INVALID_INDEX) { SkipWhitespace(cursor); const char operation = *cursor; if (operation != '+' && operation != '-') break; ++cursor; const int right = ParseMulDiv(cursor, context); if (right == INVALID_INDEX) return INVALID_INDEX; left = AddNode(operation == '+' ? ExpNode::OP_ADD : ExpNode::OP_SUB, left, right); } return left; } int ParseMulDiv(const char*& cursor, varContextType& context) { int left = ParseUnary(cursor, context); while (left != INVALID_INDEX) { SkipWhitespace(cursor); const char operation = *cursor; if (operation != '*' && operation != '/') break; ++cursor; const int right = ParseUnary(cursor, context); if (right == INVALID_INDEX) return INVALID_INDEX; left = AddNode(operation == '*' ? ExpNode::OP_MUL : ExpNode::OP_DIV, left, right); } return left; } int ParseUnary(const char*& cursor, varContextType& context) { SkipWhitespace(cursor); if (*cursor == '+') { ++cursor; return ParseUnary(cursor, context); } if (*cursor == '-') { ++cursor; const int child = ParseUnary(cursor, context); return child == INVALID_INDEX ? INVALID_INDEX : AddNode(ExpNode::OP_MUL, child, INVALID_INDEX, INVALID_INDEX, -1.0f); } return ParsePrimary(cursor, context); } int ParsePrimary(const char*& cursor, varContextType& context) { SkipWhitespace(cursor); if (*cursor == '(') { ++cursor; const int result = ParseAddSub(cursor, context); SkipWhitespace(cursor); if (*cursor != ')') return INVALID_INDEX; ++cursor; return result; } if (std::isdigit(static_cast<unsigned char>(*cursor)) != 0 || *cursor == '.') { char* numberEnd = nullptr; const float value = std::strtof(cursor, &numberEnd); if (numberEnd == cursor) return INVALID_INDEX; cursor = numberEnd; return AddNode(ExpNode::OP_VAL, INVALID_INDEX, INVALID_INDEX, INVALID_INDEX, value); } char name[128]; int nameLength = 0; while ((std::isalnum(static_cast<unsigned char>(*cursor)) != 0 || *cursor == '_' || *cursor == '.') && nameLength < 127) { name[nameLength++] = *cursor++; } name[nameLength] = '\0'; if (nameLength == 0) return INVALID_INDEX; SkipWhitespace(cursor); if (*cursor != '(') { varType variable; if (!context.LookUpVar(name, variable)) return INVALID_INDEX; return AddNode(ExpNode::OP_VAR, INVALID_INDEX, INVALID_INDEX, INVALID_INDEX, 1.0f, &variable); } ++cursor; const int first = ParseAddSub(cursor, context); SkipWhitespace(cursor); if (first == INVALID_INDEX || *cursor != ',') return INVALID_INDEX; ++cursor; const int second = ParseAddSub(cursor, context); if (second == INVALID_INDEX) return INVALID_INDEX; int third = INVALID_INDEX; SkipWhitespace(cursor); if (*cursor == ',') { ++cursor; third = ParseAddSub(cursor, context); if (third == INVALID_INDEX) return INVALID_INDEX; } SkipWhitespace(cursor); if (*cursor != ')') return INVALID_INDEX; ++cursor; typename ExpNode::op_t operation = ExpNode::OP_NONE; if (std::strcmp(name, "max") == 0 && third == INVALID_INDEX) operation = ExpNode::OP_MAX; else if (std::strcmp(name, "min") == 0 && third == INVALID_INDEX) operation = ExpNode::OP_MIN; else if (std::strcmp(name, "lerp") == 0 && third != INVALID_INDEX) operation = ExpNode::OP_LERP; else if (std::strcmp(name, "clamp") == 0 && third != INVALID_INDEX) operation = ExpNode::OP_CLAMP; if (operation == ExpNode::OP_NONE) return INVALID_INDEX; return AddNode(operation, first, second, third); } float EvalNode(const int index, varContextType& context) const { const ExpNode& node = nodes[index]; const float first = node.var0 == INVALID_INDEX ? 0.0f : EvalNode(node.var0, context); const float second = node.var1 == INVALID_INDEX ? 0.0f : EvalNode(node.var1, context); const float third = node.var2 == INVALID_INDEX ? 0.0f : EvalNode(node.var2, context); float value = 0.0f; switch (node.op) { case ExpNode::OP_VAL: value = 1.0f; break; case ExpNode::OP_VAR: value = context.GetVar(node.varId); break; case ExpNode::OP_MUL: value = node.var1 == INVALID_INDEX ? first : first * second; break; case ExpNode::OP_DIV: value = first / second; break; case ExpNode::OP_MAX: value = std::max(first, second); break; case ExpNode::OP_MIN: value = std::min(first, second); break; case ExpNode::OP_LERP: value = first + (second - first) * third; break; case ExpNode::OP_CLAMP: value = std::max(first, std::min(second, third)); break; case ExpNode::OP_SUB: value = first - second; break; case ExpNode::OP_ADD: value = first + second; break; default: value = 0.0f; break; } return node.coef * value; } idStr PrintNode(const int index) const { const ExpNode& node = nodes[index]; if (node.op == ExpNode::OP_VAL) { char number[64]; std::snprintf(number, sizeof(number), "%g", node.coef); return idStr(number); } if (node.op == ExpNode::OP_VAR) { idStr result = node.varId.GetStr(); if (node.coef != 1.0f) { char prefix[64]; std::snprintf(prefix, sizeof(prefix), "%g*", node.coef); idStr scaled(prefix); scaled.Append(result); return scaled; } return result; } const char* opName = nullptr; switch (node.op) { case ExpNode::OP_MUL: opName = "*"; break; case ExpNode::OP_DIV: opName = "/"; break; case ExpNode::OP_SUB: opName = "-"; break; case ExpNode::OP_ADD: opName = "+"; break; case ExpNode::OP_MAX: opName = "max"; break; case ExpNode::OP_MIN: opName = "min"; break; case ExpNode::OP_LERP: opName = "lerp"; break; case ExpNode::OP_CLAMP: opName = "clamp"; break; default: return idStr(); } idStr result; if (node.op == ExpNode::OP_MAX || node.op == ExpNode::OP_MIN || node.op == ExpNode::OP_LERP || node.op == ExpNode::OP_CLAMP) { result.Append(opName); result.Append('('); result.Append(PrintNode(node.var0)); result.Append(", "); result.Append(PrintNode(node.var1)); if (node.var2 != INVALID_INDEX) { result.Append(", "); result.Append(PrintNode(node.var2)); } result.Append(')'); } else if (node.var1 == INVALID_INDEX) { char coefficient[64]; std::snprintf(coefficient, sizeof(coefficient), "%g*", node.coef); result.Append(coefficient); result.Append(PrintNode(node.var0)); return result; } else { result.Append('('); result.Append(PrintNode(node.var0)); result.Append(' '); result.Append(opName); result.Append(' '); result.Append(PrintNode(node.var1)); result.Append(')'); } if (node.coef != 1.0f) { char coefficient[64]; std::snprintf(coefficient, sizeof(coefficient), "%g*", node.coef); idStr scaled(coefficient); scaled.Append(result); return scaled; } return result; } }; #if INTPTR_MAX == INT32_MAX struct idRuntimeExpressionLayoutVar { int index; idStr GetStr() const { return idStr(); } }; struct idRuntimeExpressionLayoutContext { bool LookUpVar(const char*, idRuntimeExpressionLayoutVar&) { return false; } float GetVar(const idRuntimeExpressionLayoutVar&) const { return 0.0f; } }; static_assert(sizeof(idRuntimeExpression<idRuntimeExpressionLayoutVar, idRuntimeExpressionLayoutContext>) == 20, "Recovered idRuntimeExpression ABI changed"); #endif