GPU: Shader Preprocess: Add string utilities
Allow faster and more robust parsing / mutation.
This commit is contained in:
@@ -802,6 +802,7 @@ if(WITH_GTESTS)
|
||||
tests/push_constants_test.cc
|
||||
tests/shader_create_info_test.cc
|
||||
tests/shader_test.cc
|
||||
tests/shader_preprocess_test.cc
|
||||
tests/specialization_constants_test.cc
|
||||
tests/state_blend_test.cc
|
||||
tests/storage_buffer_test.cc
|
||||
|
||||
@@ -760,6 +760,129 @@ class Preprocessor {
|
||||
suffix << "\n";
|
||||
return suffix.str();
|
||||
}
|
||||
|
||||
/* Made public for unit testing purpose. */
|
||||
public:
|
||||
static std::string get_content_between_balanced_pair(const std::string &input,
|
||||
char start_delimiter,
|
||||
char end_delimiter,
|
||||
const bool backwards = false)
|
||||
{
|
||||
int balance = 0;
|
||||
size_t start = std::string::npos;
|
||||
size_t end = std::string::npos;
|
||||
|
||||
if (backwards) {
|
||||
std::swap(start_delimiter, end_delimiter);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < input.length(); ++i) {
|
||||
size_t idx = backwards ? (input.length() - 1) - i : i;
|
||||
if (input[idx] == start_delimiter) {
|
||||
if (balance == 0) {
|
||||
start = idx;
|
||||
}
|
||||
balance++;
|
||||
}
|
||||
else if (input[idx] == end_delimiter) {
|
||||
balance--;
|
||||
if (balance == 0 && start != std::string::npos) {
|
||||
end = idx;
|
||||
if (backwards) {
|
||||
std::swap(start, end);
|
||||
}
|
||||
return input.substr(start + 1, end - start - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Replaces all occurrences of `from` by `to` between `start_delimiter`
|
||||
* and `end_delimiter` even inside nested delimiters pair. */
|
||||
static std::string replace_char_between_balanced_pair(const std::string &input,
|
||||
const char start_delimiter,
|
||||
const char end_delimiter,
|
||||
const char from,
|
||||
const char to)
|
||||
{
|
||||
int depth = 0;
|
||||
|
||||
std::string str = input;
|
||||
for (char &string_char : str) {
|
||||
if (string_char == start_delimiter) {
|
||||
depth++;
|
||||
}
|
||||
else if (string_char == end_delimiter) {
|
||||
depth--;
|
||||
}
|
||||
else if (depth > 0 && string_char == from) {
|
||||
string_char = to;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/* Function to split a string by a delimiter and return a vector of substrings. */
|
||||
static std::vector<std::string> split_string(const std::string &str, const char delimiter)
|
||||
{
|
||||
std::vector<std::string> substrings;
|
||||
std::stringstream ss(str);
|
||||
std::string item;
|
||||
|
||||
while (std::getline(ss, item, delimiter)) {
|
||||
substrings.push_back(item);
|
||||
}
|
||||
return substrings;
|
||||
}
|
||||
|
||||
/* Similar to split_string but only split if the delimiter is not between any pair_start and
|
||||
* pair_end. */
|
||||
static std::vector<std::string> split_string_not_between_balanced_pair(const std::string &str,
|
||||
const char delimiter,
|
||||
const char pair_start,
|
||||
const char pair_end)
|
||||
{
|
||||
const char safe_char = '@';
|
||||
const std::string safe_str = replace_char_between_balanced_pair(
|
||||
str, pair_start, pair_end, delimiter, safe_char);
|
||||
std::vector<std::string> split = split_string(safe_str, delimiter);
|
||||
for (std::string &str : split) {
|
||||
replace_all(str, safe_char, delimiter);
|
||||
}
|
||||
return split;
|
||||
}
|
||||
|
||||
static void replace_all(std::string &str, const std::string &from, const std::string &to)
|
||||
{
|
||||
if (from.empty()) {
|
||||
return;
|
||||
}
|
||||
size_t start_pos = 0;
|
||||
while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||
str.replace(start_pos, from.length(), to);
|
||||
start_pos += to.length();
|
||||
}
|
||||
}
|
||||
|
||||
static void replace_all(std::string &str, const char from, const char to)
|
||||
{
|
||||
for (char &string_char : str) {
|
||||
if (string_char == from) {
|
||||
string_char = to;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int64_t char_count(const std::string &str, char c)
|
||||
{
|
||||
return std::count(str.begin(), str.end(), c);
|
||||
}
|
||||
|
||||
static int64_t line_count(const std::string &str)
|
||||
{
|
||||
return char_count(str, '\n');
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::gpu::shader
|
||||
|
||||
35
source/blender/gpu/tests/shader_preprocess_test.cc
Normal file
35
source/blender/gpu/tests/shader_preprocess_test.cc
Normal file
@@ -0,0 +1,35 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "glsl_preprocess/glsl_preprocess.hh"
|
||||
|
||||
#include "gpu_testing.hh"
|
||||
|
||||
namespace blender::gpu::tests {
|
||||
|
||||
static void test_preprocess_utilities()
|
||||
{
|
||||
using namespace shader;
|
||||
using namespace std;
|
||||
/* get_content_between_balanced_pair */
|
||||
string input = "test (u, u(s,(s,s)), u) {t{{}},t,{};(,)} {u{}} end";
|
||||
EXPECT_EQ(Preprocessor::get_content_between_balanced_pair(input, '{', '}'), "t{{}},t,{};(,)");
|
||||
EXPECT_EQ(Preprocessor::get_content_between_balanced_pair(input, '{', '}', true), "u{}");
|
||||
|
||||
EXPECT_EQ(Preprocessor::replace_char_between_balanced_pair(input, '(', ')', ',', '!'),
|
||||
"test (u! u(s!(s!s))! u) {t{{}},t,{};(!)} {u{}} end");
|
||||
|
||||
vector<string> split_expect{"test (u, u(s,(s,s", "", ", u", " {t{{}},t,{};(,", "} {u{}} end"};
|
||||
vector<string> split_result = Preprocessor::split_string(input, ')');
|
||||
EXPECT_EQ_VECTOR(split_expect, split_result);
|
||||
|
||||
string input2 = "u, u(s,(s,s)), u";
|
||||
vector<string> split_expect2{"u", " u(s,(s,s))", " u"};
|
||||
vector<string> split_result2 = Preprocessor::split_string_not_between_balanced_pair(
|
||||
input2, ',', '(', ')');
|
||||
EXPECT_EQ_VECTOR(split_expect2, split_result2);
|
||||
}
|
||||
GPU_TEST(preprocess_utilities);
|
||||
|
||||
} // namespace blender::gpu::tests
|
||||
Reference in New Issue
Block a user