feat: godot-engine-source-4.3-stable

This commit is contained in:
Jan van der Weide 2025-01-17 16:36:38 +01:00
parent c59a7dcade
commit 7125d019b5
11149 changed files with 5070401 additions and 0 deletions

7
engine/core/string/SCsub Normal file
View file

@ -0,0 +1,7 @@
#!/usr/bin/env python
Import("env")
env_string = env.Clone()
env_string.add_source_files(env.core_sources, "*.cpp")

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,135 @@
/**************************************************************************/
/* char_utils.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef CHAR_UTILS_H
#define CHAR_UTILS_H
#include "core/typedefs.h"
#include "char_range.inc"
#define BSEARCH_CHAR_RANGE(m_array) \
int low = 0; \
int high = sizeof(m_array) / sizeof(m_array[0]) - 1; \
int middle; \
\
while (low <= high) { \
middle = (low + high) / 2; \
\
if (c < m_array[middle].start) { \
high = middle - 1; \
} else if (c > m_array[middle].end) { \
low = middle + 1; \
} else { \
return true; \
} \
} \
\
return false
static _FORCE_INLINE_ bool is_unicode_identifier_start(char32_t c) {
BSEARCH_CHAR_RANGE(xid_start);
}
static _FORCE_INLINE_ bool is_unicode_identifier_continue(char32_t c) {
BSEARCH_CHAR_RANGE(xid_continue);
}
static _FORCE_INLINE_ bool is_unicode_upper_case(char32_t c) {
BSEARCH_CHAR_RANGE(uppercase_letter);
}
static _FORCE_INLINE_ bool is_unicode_lower_case(char32_t c) {
BSEARCH_CHAR_RANGE(lowercase_letter);
}
static _FORCE_INLINE_ bool is_unicode_letter(char32_t c) {
BSEARCH_CHAR_RANGE(unicode_letter);
}
#undef BSEARCH_CHAR_RANGE
static _FORCE_INLINE_ bool is_ascii_upper_case(char32_t c) {
return (c >= 'A' && c <= 'Z');
}
static _FORCE_INLINE_ bool is_ascii_lower_case(char32_t c) {
return (c >= 'a' && c <= 'z');
}
static _FORCE_INLINE_ bool is_digit(char32_t c) {
return (c >= '0' && c <= '9');
}
static _FORCE_INLINE_ bool is_hex_digit(char32_t c) {
return (is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
}
static _FORCE_INLINE_ bool is_binary_digit(char32_t c) {
return (c == '0' || c == '1');
}
static _FORCE_INLINE_ bool is_ascii_alphabet_char(char32_t c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
static _FORCE_INLINE_ bool is_ascii_alphanumeric_char(char32_t c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9');
}
static _FORCE_INLINE_ bool is_ascii_identifier_char(char32_t c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
}
static _FORCE_INLINE_ bool is_symbol(char32_t c) {
return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' ');
}
static _FORCE_INLINE_ bool is_control(char32_t p_char) {
return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009f);
}
static _FORCE_INLINE_ bool is_whitespace(char32_t p_char) {
return (p_char == ' ') || (p_char == 0x00a0) || (p_char == 0x1680) || (p_char >= 0x2000 && p_char <= 0x200a) || (p_char == 0x202f) || (p_char == 0x205f) || (p_char == 0x3000) || (p_char == 0x2028) || (p_char == 0x2029) || (p_char >= 0x0009 && p_char <= 0x000d) || (p_char == 0x0085);
}
static _FORCE_INLINE_ bool is_linebreak(char32_t p_char) {
return (p_char >= 0x000a && p_char <= 0x000d) || (p_char == 0x0085) || (p_char == 0x2028) || (p_char == 0x2029);
}
static _FORCE_INLINE_ bool is_punct(char32_t p_char) {
return (p_char >= ' ' && p_char <= '/') || (p_char >= ':' && p_char <= '@') || (p_char >= '[' && p_char <= '^') || (p_char == '`') || (p_char >= '{' && p_char <= '~') || (p_char >= 0x2000 && p_char <= 0x206f) || (p_char >= 0x3000 && p_char <= 0x303f);
}
static _FORCE_INLINE_ bool is_underscore(char32_t p_char) {
return (p_char == '_');
}
#endif // CHAR_UTILS_H

1199
engine/core/string/locales.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,475 @@
/**************************************************************************/
/* node_path.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "node_path.h"
#include "core/string/print_string.h"
void NodePath::_update_hash_cache() const {
uint32_t h = data->absolute ? 1 : 0;
int pc = data->path.size();
const StringName *sn = data->path.ptr();
for (int i = 0; i < pc; i++) {
h = h ^ sn[i].hash();
}
int spc = data->subpath.size();
const StringName *ssn = data->subpath.ptr();
for (int i = 0; i < spc; i++) {
h = h ^ ssn[i].hash();
}
data->hash_cache_valid = true;
data->hash_cache = h;
}
void NodePath::prepend_period() {
if (data->path.size() && data->path[0].operator String() != ".") {
data->path.insert(0, ".");
data->hash_cache_valid = false;
}
}
bool NodePath::is_absolute() const {
if (!data) {
return false;
}
return data->absolute;
}
int NodePath::get_name_count() const {
if (!data) {
return 0;
}
return data->path.size();
}
StringName NodePath::get_name(int p_idx) const {
ERR_FAIL_NULL_V(data, StringName());
ERR_FAIL_INDEX_V(p_idx, data->path.size(), StringName());
return data->path[p_idx];
}
int NodePath::get_subname_count() const {
if (!data) {
return 0;
}
return data->subpath.size();
}
StringName NodePath::get_subname(int p_idx) const {
ERR_FAIL_NULL_V(data, StringName());
ERR_FAIL_INDEX_V(p_idx, data->subpath.size(), StringName());
return data->subpath[p_idx];
}
int NodePath::get_total_name_count() const {
if (!data) {
return 0;
}
return data->path.size() + data->subpath.size();
}
void NodePath::unref() {
if (data && data->refcount.unref()) {
memdelete(data);
}
data = nullptr;
}
bool NodePath::operator==(const NodePath &p_path) const {
if (data == p_path.data) {
return true;
}
if (!data || !p_path.data) {
return false;
}
if (data->absolute != p_path.data->absolute) {
return false;
}
int path_size = data->path.size();
if (path_size != p_path.data->path.size()) {
return false;
}
int subpath_size = data->subpath.size();
if (subpath_size != p_path.data->subpath.size()) {
return false;
}
const StringName *l_path_ptr = data->path.ptr();
const StringName *r_path_ptr = p_path.data->path.ptr();
for (int i = 0; i < path_size; i++) {
if (l_path_ptr[i] != r_path_ptr[i]) {
return false;
}
}
const StringName *l_subpath_ptr = data->subpath.ptr();
const StringName *r_subpath_ptr = p_path.data->subpath.ptr();
for (int i = 0; i < subpath_size; i++) {
if (l_subpath_ptr[i] != r_subpath_ptr[i]) {
return false;
}
}
return true;
}
bool NodePath::operator!=(const NodePath &p_path) const {
return (!(*this == p_path));
}
void NodePath::operator=(const NodePath &p_path) {
if (this == &p_path) {
return;
}
unref();
if (p_path.data && p_path.data->refcount.ref()) {
data = p_path.data;
}
}
NodePath::operator String() const {
if (!data) {
return String();
}
String ret;
if (data->absolute) {
ret = "/";
}
for (int i = 0; i < data->path.size(); i++) {
if (i > 0) {
ret += "/";
}
ret += data->path[i].operator String();
}
for (int i = 0; i < data->subpath.size(); i++) {
ret += ":" + data->subpath[i].operator String();
}
return ret;
}
Vector<StringName> NodePath::get_names() const {
if (data) {
return data->path;
}
return Vector<StringName>();
}
Vector<StringName> NodePath::get_subnames() const {
if (data) {
return data->subpath;
}
return Vector<StringName>();
}
StringName NodePath::get_concatenated_names() const {
ERR_FAIL_NULL_V(data, StringName());
if (!data->concatenated_path) {
int pc = data->path.size();
String concatenated;
const StringName *sn = data->path.ptr();
for (int i = 0; i < pc; i++) {
concatenated += i == 0 ? sn[i].operator String() : "/" + sn[i];
}
data->concatenated_path = concatenated;
}
return data->concatenated_path;
}
StringName NodePath::get_concatenated_subnames() const {
ERR_FAIL_NULL_V(data, StringName());
if (!data->concatenated_subpath) {
int spc = data->subpath.size();
String concatenated;
const StringName *ssn = data->subpath.ptr();
for (int i = 0; i < spc; i++) {
concatenated += i == 0 ? ssn[i].operator String() : ":" + ssn[i];
}
data->concatenated_subpath = concatenated;
}
return data->concatenated_subpath;
}
NodePath NodePath::slice(int p_begin, int p_end) const {
const int name_count = get_name_count();
const int total_count = get_total_name_count();
int begin = CLAMP(p_begin, -total_count, total_count);
if (begin < 0) {
begin += total_count;
}
int end = CLAMP(p_end, -total_count, total_count);
if (end < 0) {
end += total_count;
}
const int sub_begin = MAX(begin - name_count - 1, 0);
const int sub_end = MAX(end - name_count, 0);
const Vector<StringName> names = get_names().slice(begin, end);
const Vector<StringName> sub_names = get_subnames().slice(sub_begin, sub_end);
const bool absolute = is_absolute() && (begin == 0);
return NodePath(names, sub_names, absolute);
}
NodePath NodePath::rel_path_to(const NodePath &p_np) const {
ERR_FAIL_COND_V(!is_absolute(), NodePath());
ERR_FAIL_COND_V(!p_np.is_absolute(), NodePath());
Vector<StringName> src_dirs = get_names();
Vector<StringName> dst_dirs = p_np.get_names();
//find common parent
int common_parent = 0;
while (true) {
if (src_dirs.size() == common_parent) {
break;
}
if (dst_dirs.size() == common_parent) {
break;
}
if (src_dirs[common_parent] != dst_dirs[common_parent]) {
break;
}
common_parent++;
}
common_parent--;
Vector<StringName> relpath;
relpath.resize(src_dirs.size() + dst_dirs.size() + 1);
StringName *relpath_ptr = relpath.ptrw();
int path_size = 0;
StringName back_str("..");
for (int i = common_parent + 1; i < src_dirs.size(); i++) {
relpath_ptr[path_size++] = back_str;
}
for (int i = common_parent + 1; i < dst_dirs.size(); i++) {
relpath_ptr[path_size++] = dst_dirs[i];
}
if (path_size == 0) {
relpath_ptr[path_size++] = ".";
}
relpath.resize(path_size);
return NodePath(relpath, p_np.get_subnames(), false);
}
NodePath NodePath::get_as_property_path() const {
if (!data || !data->path.size()) {
return *this;
} else {
Vector<StringName> new_path = data->subpath;
String initial_subname = data->path[0];
for (int i = 1; i < data->path.size(); i++) {
initial_subname += "/" + data->path[i];
}
new_path.insert(0, initial_subname);
return NodePath(Vector<StringName>(), new_path, false);
}
}
bool NodePath::is_empty() const {
return !data;
}
void NodePath::simplify() {
if (!data) {
return;
}
for (int i = 0; i < data->path.size(); i++) {
if (data->path.size() == 1) {
break;
}
if (data->path[i].operator String() == ".") {
data->path.remove_at(i);
i--;
} else if (i > 0 && data->path[i].operator String() == ".." && data->path[i - 1].operator String() != "." && data->path[i - 1].operator String() != "..") {
//remove both
data->path.remove_at(i - 1);
data->path.remove_at(i - 1);
i -= 2;
if (data->path.size() == 0) {
data->path.push_back(".");
break;
}
}
}
data->hash_cache_valid = false;
}
NodePath NodePath::simplified() const {
NodePath np = *this;
np.simplify();
return np;
}
NodePath::NodePath(const Vector<StringName> &p_path, bool p_absolute) {
if (p_path.size() == 0 && !p_absolute) {
return;
}
data = memnew(Data);
data->refcount.init();
data->absolute = p_absolute;
data->path = p_path;
data->hash_cache_valid = false;
}
NodePath::NodePath(const Vector<StringName> &p_path, const Vector<StringName> &p_subpath, bool p_absolute) {
if (p_path.size() == 0 && p_subpath.size() == 0 && !p_absolute) {
return;
}
data = memnew(Data);
data->refcount.init();
data->absolute = p_absolute;
data->path = p_path;
data->subpath = p_subpath;
data->hash_cache_valid = false;
}
NodePath::NodePath(const NodePath &p_path) {
if (p_path.data && p_path.data->refcount.ref()) {
data = p_path.data;
}
}
NodePath::NodePath(const String &p_path) {
if (p_path.length() == 0) {
return;
}
String path = p_path;
Vector<StringName> subpath;
bool absolute = (path[0] == '/');
bool last_is_slash = true;
int slices = 0;
int subpath_pos = path.find(":");
if (subpath_pos != -1) {
int from = subpath_pos + 1;
for (int i = from; i <= path.length(); i++) {
if (path[i] == ':' || path[i] == 0) {
String str = path.substr(from, i - from);
if (str.is_empty()) {
if (path[i] == 0) {
continue; // Allow end-of-path :
}
ERR_FAIL_MSG("Invalid NodePath '" + p_path + "'.");
}
subpath.push_back(str);
from = i + 1;
}
}
path = path.substr(0, subpath_pos);
}
for (int i = (int)absolute; i < path.length(); i++) {
if (path[i] == '/') {
last_is_slash = true;
} else {
if (last_is_slash) {
slices++;
}
last_is_slash = false;
}
}
if (slices == 0 && !absolute && !subpath.size()) {
return;
}
data = memnew(Data);
data->refcount.init();
data->absolute = absolute;
data->subpath = subpath;
data->hash_cache_valid = false;
if (slices == 0) {
return;
}
data->path.resize(slices);
last_is_slash = true;
int from = (int)absolute;
int slice = 0;
for (int i = (int)absolute; i < path.length() + 1; i++) {
if (path[i] == '/' || path[i] == 0) {
if (!last_is_slash) {
String name = path.substr(from, i - from);
ERR_FAIL_INDEX(slice, data->path.size());
data->path.write[slice++] = name;
}
from = i + 1;
last_is_slash = true;
} else {
last_is_slash = false;
}
}
}
NodePath::~NodePath() {
unref();
}

View file

@ -0,0 +1,100 @@
/**************************************************************************/
/* node_path.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef NODE_PATH_H
#define NODE_PATH_H
#include "core/string/string_name.h"
#include "core/string/ustring.h"
class NodePath {
struct Data {
SafeRefCount refcount;
Vector<StringName> path;
Vector<StringName> subpath;
StringName concatenated_path;
StringName concatenated_subpath;
bool absolute;
mutable bool hash_cache_valid;
mutable uint32_t hash_cache;
};
mutable Data *data = nullptr;
void unref();
void _update_hash_cache() const;
public:
bool is_absolute() const;
int get_name_count() const;
StringName get_name(int p_idx) const;
int get_subname_count() const;
StringName get_subname(int p_idx) const;
int get_total_name_count() const;
Vector<StringName> get_names() const;
Vector<StringName> get_subnames() const;
StringName get_concatenated_names() const;
StringName get_concatenated_subnames() const;
NodePath slice(int p_begin, int p_end = INT_MAX) const;
NodePath rel_path_to(const NodePath &p_np) const;
NodePath get_as_property_path() const;
void prepend_period();
_FORCE_INLINE_ uint32_t hash() const {
if (!data) {
return 0;
}
if (!data->hash_cache_valid) {
_update_hash_cache();
}
return data->hash_cache;
}
operator String() const;
bool is_empty() const;
bool operator==(const NodePath &p_path) const;
bool operator!=(const NodePath &p_path) const;
void operator=(const NodePath &p_path);
void simplify();
NodePath simplified() const;
NodePath(const Vector<StringName> &p_path, bool p_absolute);
NodePath(const Vector<StringName> &p_path, const Vector<StringName> &p_subpath, bool p_absolute);
NodePath(const NodePath &p_path);
NodePath(const String &p_path);
NodePath() {}
~NodePath();
};
#endif // NODE_PATH_H

View file

@ -0,0 +1,317 @@
/**************************************************************************/
/* optimized_translation.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "optimized_translation.h"
#include "core/templates/pair.h"
extern "C" {
#include "thirdparty/misc/smaz.h"
}
struct CompressedString {
int orig_len = 0;
CharString compressed;
int offset = 0;
};
void OptimizedTranslation::generate(const Ref<Translation> &p_from) {
// This method compresses a Translation instance.
// Right now, it doesn't handle context or plurals, so Translation subclasses using plurals or context (i.e TranslationPO) shouldn't be compressed.
#ifdef TOOLS_ENABLED
ERR_FAIL_COND(p_from.is_null());
List<StringName> keys;
p_from->get_message_list(&keys);
int size = Math::larger_prime(keys.size());
Vector<Vector<Pair<int, CharString>>> buckets;
Vector<HashMap<uint32_t, int>> table;
Vector<uint32_t> hfunc_table;
Vector<CompressedString> compressed;
table.resize(size);
hfunc_table.resize(size);
buckets.resize(size);
compressed.resize(keys.size());
int idx = 0;
int total_compression_size = 0;
for (const StringName &E : keys) {
//hash string
CharString cs = E.operator String().utf8();
uint32_t h = hash(0, cs.get_data());
Pair<int, CharString> p;
p.first = idx;
p.second = cs;
buckets.write[h % size].push_back(p);
//compress string
CharString src_s = p_from->get_message(E).operator String().utf8();
CompressedString ps;
ps.orig_len = src_s.size();
ps.offset = total_compression_size;
if (ps.orig_len != 0) {
CharString dst_s;
dst_s.resize(src_s.size());
int ret = smaz_compress(src_s.get_data(), src_s.size(), dst_s.ptrw(), src_s.size());
if (ret >= src_s.size()) {
//if compressed is larger than original, just use original
ps.orig_len = src_s.size();
ps.compressed = src_s;
} else {
dst_s.resize(ret);
//ps.orig_len=;
ps.compressed = dst_s;
}
} else {
ps.orig_len = 1;
ps.compressed.resize(1);
ps.compressed[0] = 0;
}
compressed.write[idx] = ps;
total_compression_size += ps.compressed.size();
idx++;
}
int bucket_table_size = 0;
for (int i = 0; i < size; i++) {
const Vector<Pair<int, CharString>> &b = buckets[i];
HashMap<uint32_t, int> &t = table.write[i];
if (b.size() == 0) {
continue;
}
int d = 1;
int item = 0;
while (item < b.size()) {
uint32_t slot = hash(d, b[item].second.get_data());
if (t.has(slot)) {
item = 0;
d++;
t.clear();
} else {
t[slot] = b[item].first;
item++;
}
}
hfunc_table.write[i] = d;
bucket_table_size += 2 + b.size() * 4;
}
ERR_FAIL_COND(bucket_table_size == 0);
hash_table.resize(size);
bucket_table.resize(bucket_table_size);
int *htwb = hash_table.ptrw();
int *btwb = bucket_table.ptrw();
uint32_t *htw = (uint32_t *)&htwb[0];
uint32_t *btw = (uint32_t *)&btwb[0];
int btindex = 0;
for (int i = 0; i < size; i++) {
const HashMap<uint32_t, int> &t = table[i];
if (t.size() == 0) {
htw[i] = 0xFFFFFFFF; //nothing
continue;
}
htw[i] = btindex;
btw[btindex++] = t.size();
btw[btindex++] = hfunc_table[i];
for (const KeyValue<uint32_t, int> &E : t) {
btw[btindex++] = E.key;
btw[btindex++] = compressed[E.value].offset;
btw[btindex++] = compressed[E.value].compressed.size();
btw[btindex++] = compressed[E.value].orig_len;
}
}
strings.resize(total_compression_size);
uint8_t *cw = strings.ptrw();
for (int i = 0; i < compressed.size(); i++) {
memcpy(&cw[compressed[i].offset], compressed[i].compressed.get_data(), compressed[i].compressed.size());
}
ERR_FAIL_COND(btindex != bucket_table_size);
set_locale(p_from->get_locale());
#endif
}
bool OptimizedTranslation::_set(const StringName &p_name, const Variant &p_value) {
String prop_name = p_name.operator String();
if (prop_name == "hash_table") {
hash_table = p_value;
} else if (prop_name == "bucket_table") {
bucket_table = p_value;
} else if (prop_name == "strings") {
strings = p_value;
} else if (prop_name == "load_from") {
generate(p_value);
} else {
return false;
}
return true;
}
bool OptimizedTranslation::_get(const StringName &p_name, Variant &r_ret) const {
String prop_name = p_name.operator String();
if (prop_name == "hash_table") {
r_ret = hash_table;
} else if (prop_name == "bucket_table") {
r_ret = bucket_table;
} else if (prop_name == "strings") {
r_ret = strings;
} else {
return false;
}
return true;
}
StringName OptimizedTranslation::get_message(const StringName &p_src_text, const StringName &p_context) const {
// p_context passed in is ignore. The use of context is not yet supported in OptimizedTranslation.
int htsize = hash_table.size();
if (htsize == 0) {
return StringName();
}
CharString str = p_src_text.operator String().utf8();
uint32_t h = hash(0, str.get_data());
const int *htr = hash_table.ptr();
const uint32_t *htptr = (const uint32_t *)&htr[0];
const int *btr = bucket_table.ptr();
const uint32_t *btptr = (const uint32_t *)&btr[0];
const uint8_t *sr = strings.ptr();
const char *sptr = (const char *)&sr[0];
uint32_t p = htptr[h % htsize];
if (p == 0xFFFFFFFF) {
return StringName(); //nothing
}
const Bucket &bucket = *(const Bucket *)&btptr[p];
h = hash(bucket.func, str.get_data());
int idx = -1;
for (int i = 0; i < bucket.size; i++) {
if (bucket.elem[i].key == h) {
idx = i;
break;
}
}
if (idx == -1) {
return StringName();
}
if (bucket.elem[idx].comp_size == bucket.elem[idx].uncomp_size) {
String rstr;
rstr.parse_utf8(&sptr[bucket.elem[idx].str_offset], bucket.elem[idx].uncomp_size);
return rstr;
} else {
CharString uncomp;
uncomp.resize(bucket.elem[idx].uncomp_size + 1);
smaz_decompress(&sptr[bucket.elem[idx].str_offset], bucket.elem[idx].comp_size, uncomp.ptrw(), bucket.elem[idx].uncomp_size);
String rstr;
rstr.parse_utf8(uncomp.get_data());
return rstr;
}
}
Vector<String> OptimizedTranslation::get_translated_message_list() const {
Vector<String> msgs;
const int *htr = hash_table.ptr();
const uint32_t *htptr = (const uint32_t *)&htr[0];
const int *btr = bucket_table.ptr();
const uint32_t *btptr = (const uint32_t *)&btr[0];
const uint8_t *sr = strings.ptr();
const char *sptr = (const char *)&sr[0];
for (int i = 0; i < hash_table.size(); i++) {
uint32_t p = htptr[i];
if (p != 0xFFFFFFFF) {
const Bucket &bucket = *(const Bucket *)&btptr[p];
for (int j = 0; j < bucket.size; j++) {
if (bucket.elem[j].comp_size == bucket.elem[j].uncomp_size) {
String rstr;
rstr.parse_utf8(&sptr[bucket.elem[j].str_offset], bucket.elem[j].uncomp_size);
msgs.push_back(rstr);
} else {
CharString uncomp;
uncomp.resize(bucket.elem[j].uncomp_size + 1);
smaz_decompress(&sptr[bucket.elem[j].str_offset], bucket.elem[j].comp_size, uncomp.ptrw(), bucket.elem[j].uncomp_size);
String rstr;
rstr.parse_utf8(uncomp.get_data());
msgs.push_back(rstr);
}
}
}
}
return msgs;
}
StringName OptimizedTranslation::get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context) const {
// The use of plurals translation is not yet supported in OptimizedTranslation.
return get_message(p_src_text, p_context);
}
void OptimizedTranslation::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, "hash_table"));
p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, "bucket_table"));
p_list->push_back(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "strings"));
p_list->push_back(PropertyInfo(Variant::OBJECT, "load_from", PROPERTY_HINT_RESOURCE_TYPE, "Translation", PROPERTY_USAGE_EDITOR));
}
void OptimizedTranslation::_bind_methods() {
ClassDB::bind_method(D_METHOD("generate", "from"), &OptimizedTranslation::generate);
}

View file

@ -0,0 +1,90 @@
/**************************************************************************/
/* optimized_translation.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef OPTIMIZED_TRANSLATION_H
#define OPTIMIZED_TRANSLATION_H
#include "core/string/translation.h"
class OptimizedTranslation : public Translation {
GDCLASS(OptimizedTranslation, Translation);
//this translation uses a sort of modified perfect hash algorithm
//it requires hashing strings twice and then does a binary search,
//so it's slower, but at the same time it has an extremely high chance
//of catching untranslated strings
//load/store friendly types
Vector<int> hash_table;
Vector<int> bucket_table;
Vector<uint8_t> strings;
struct Bucket {
int size;
uint32_t func;
struct Elem {
uint32_t key;
uint32_t str_offset;
uint32_t comp_size;
uint32_t uncomp_size;
};
Elem elem[1];
};
_FORCE_INLINE_ uint32_t hash(uint32_t d, const char *p_str) const {
if (d == 0) {
d = 0x1000193;
}
while (*p_str) {
d = (d * 0x1000193) ^ uint32_t(*p_str);
p_str++;
}
return d;
}
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
public:
virtual StringName get_message(const StringName &p_src_text, const StringName &p_context = "") const override; //overridable for other implementations
virtual StringName get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context = "") const override;
virtual Vector<String> get_translated_message_list() const override;
void generate(const Ref<Translation> &p_from);
OptimizedTranslation() {}
};
#endif // OPTIMIZED_TRANSLATION_H

View file

@ -0,0 +1,204 @@
/**************************************************************************/
/* print_string.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "print_string.h"
#include "core/core_globals.h"
#include "core/os/os.h"
#include <stdio.h>
static PrintHandlerList *print_handler_list = nullptr;
void add_print_handler(PrintHandlerList *p_handler) {
_global_lock();
p_handler->next = print_handler_list;
print_handler_list = p_handler;
_global_unlock();
}
void remove_print_handler(const PrintHandlerList *p_handler) {
_global_lock();
PrintHandlerList *prev = nullptr;
PrintHandlerList *l = print_handler_list;
while (l) {
if (l == p_handler) {
if (prev) {
prev->next = l->next;
} else {
print_handler_list = l->next;
}
break;
}
prev = l;
l = l->next;
}
//OS::get_singleton()->print("print handler list is %p\n",print_handler_list);
_global_unlock();
ERR_FAIL_NULL(l);
}
void __print_line(const String &p_string) {
if (!CoreGlobals::print_line_enabled) {
return;
}
OS::get_singleton()->print("%s\n", p_string.utf8().get_data());
_global_lock();
PrintHandlerList *l = print_handler_list;
while (l) {
l->printfunc(l->userdata, p_string, false, false);
l = l->next;
}
_global_unlock();
}
void __print_line_rich(const String &p_string) {
if (!CoreGlobals::print_line_enabled) {
return;
}
// Convert a subset of BBCode tags to ANSI escape codes for correct display in the terminal.
// Support of those ANSI escape codes varies across terminal emulators,
// especially for italic and strikethrough.
String p_string_ansi = p_string;
p_string_ansi = p_string_ansi.replace("[b]", "\u001b[1m");
p_string_ansi = p_string_ansi.replace("[/b]", "\u001b[22m");
p_string_ansi = p_string_ansi.replace("[i]", "\u001b[3m");
p_string_ansi = p_string_ansi.replace("[/i]", "\u001b[23m");
p_string_ansi = p_string_ansi.replace("[u]", "\u001b[4m");
p_string_ansi = p_string_ansi.replace("[/u]", "\u001b[24m");
p_string_ansi = p_string_ansi.replace("[s]", "\u001b[9m");
p_string_ansi = p_string_ansi.replace("[/s]", "\u001b[29m");
p_string_ansi = p_string_ansi.replace("[indent]", " ");
p_string_ansi = p_string_ansi.replace("[/indent]", "");
p_string_ansi = p_string_ansi.replace("[code]", "\u001b[2m");
p_string_ansi = p_string_ansi.replace("[/code]", "\u001b[22m");
p_string_ansi = p_string_ansi.replace("[url]", "");
p_string_ansi = p_string_ansi.replace("[/url]", "");
p_string_ansi = p_string_ansi.replace("[center]", "\n\t\t\t");
p_string_ansi = p_string_ansi.replace("[/center]", "");
p_string_ansi = p_string_ansi.replace("[right]", "\n\t\t\t\t\t\t");
p_string_ansi = p_string_ansi.replace("[/right]", "");
if (p_string_ansi.contains("[color")) {
p_string_ansi = p_string_ansi.replace("[color=black]", "\u001b[30m");
p_string_ansi = p_string_ansi.replace("[color=red]", "\u001b[91m");
p_string_ansi = p_string_ansi.replace("[color=green]", "\u001b[92m");
p_string_ansi = p_string_ansi.replace("[color=lime]", "\u001b[92m");
p_string_ansi = p_string_ansi.replace("[color=yellow]", "\u001b[93m");
p_string_ansi = p_string_ansi.replace("[color=blue]", "\u001b[94m");
p_string_ansi = p_string_ansi.replace("[color=magenta]", "\u001b[95m");
p_string_ansi = p_string_ansi.replace("[color=pink]", "\u001b[38;5;218m");
p_string_ansi = p_string_ansi.replace("[color=purple]", "\u001b[38;5;98m");
p_string_ansi = p_string_ansi.replace("[color=cyan]", "\u001b[96m");
p_string_ansi = p_string_ansi.replace("[color=white]", "\u001b[97m");
p_string_ansi = p_string_ansi.replace("[color=orange]", "\u001b[38;5;208m");
p_string_ansi = p_string_ansi.replace("[color=gray]", "\u001b[90m");
p_string_ansi = p_string_ansi.replace("[/color]", "\u001b[39m");
}
if (p_string_ansi.contains("[bgcolor")) {
p_string_ansi = p_string_ansi.replace("[bgcolor=black]", "\u001b[40m");
p_string_ansi = p_string_ansi.replace("[bgcolor=red]", "\u001b[101m");
p_string_ansi = p_string_ansi.replace("[bgcolor=green]", "\u001b[102m");
p_string_ansi = p_string_ansi.replace("[bgcolor=lime]", "\u001b[102m");
p_string_ansi = p_string_ansi.replace("[bgcolor=yellow]", "\u001b[103m");
p_string_ansi = p_string_ansi.replace("[bgcolor=blue]", "\u001b[104m");
p_string_ansi = p_string_ansi.replace("[bgcolor=magenta]", "\u001b[105m");
p_string_ansi = p_string_ansi.replace("[bgcolor=pink]", "\u001b[48;5;218m");
p_string_ansi = p_string_ansi.replace("[bgcolor=purple]", "\u001b[48;5;98m");
p_string_ansi = p_string_ansi.replace("[bgcolor=cyan]", "\u001b[106m");
p_string_ansi = p_string_ansi.replace("[bgcolor=white]", "\u001b[107m");
p_string_ansi = p_string_ansi.replace("[bgcolor=orange]", "\u001b[48;5;208m");
p_string_ansi = p_string_ansi.replace("[bgcolor=gray]", "\u001b[100m");
p_string_ansi = p_string_ansi.replace("[/bgcolor]", "\u001b[49m");
}
if (p_string_ansi.contains("[fgcolor")) {
p_string_ansi = p_string_ansi.replace("[fgcolor=black]", "\u001b[30;40m");
p_string_ansi = p_string_ansi.replace("[fgcolor=red]", "\u001b[91;101m");
p_string_ansi = p_string_ansi.replace("[fgcolor=green]", "\u001b[92;102m");
p_string_ansi = p_string_ansi.replace("[fgcolor=lime]", "\u001b[92;102m");
p_string_ansi = p_string_ansi.replace("[fgcolor=yellow]", "\u001b[93;103m");
p_string_ansi = p_string_ansi.replace("[fgcolor=blue]", "\u001b[94;104m");
p_string_ansi = p_string_ansi.replace("[fgcolor=magenta]", "\u001b[95;105m");
p_string_ansi = p_string_ansi.replace("[fgcolor=pink]", "\u001b[38;5;218;48;5;218m");
p_string_ansi = p_string_ansi.replace("[fgcolor=purple]", "\u001b[38;5;98;48;5;98m");
p_string_ansi = p_string_ansi.replace("[fgcolor=cyan]", "\u001b[96;106m");
p_string_ansi = p_string_ansi.replace("[fgcolor=white]", "\u001b[97;107m");
p_string_ansi = p_string_ansi.replace("[fgcolor=orange]", "\u001b[38;5;208;48;5;208m");
p_string_ansi = p_string_ansi.replace("[fgcolor=gray]", "\u001b[90;100m");
p_string_ansi = p_string_ansi.replace("[/fgcolor]", "\u001b[39;49m");
}
p_string_ansi += "\u001b[0m"; // Reset.
OS::get_singleton()->print_rich("%s\n", p_string_ansi.utf8().get_data());
_global_lock();
PrintHandlerList *l = print_handler_list;
while (l) {
l->printfunc(l->userdata, p_string, false, true);
l = l->next;
}
_global_unlock();
}
void print_error(const String &p_string) {
if (!CoreGlobals::print_error_enabled) {
return;
}
OS::get_singleton()->printerr("%s\n", p_string.utf8().get_data());
_global_lock();
PrintHandlerList *l = print_handler_list;
while (l) {
l->printfunc(l->userdata, p_string, true, false);
l = l->next;
}
_global_unlock();
}
bool is_print_verbose_enabled() {
return OS::get_singleton()->is_stdout_verbose();
}
String stringify_variants(const Variant &p_var) {
return p_var.operator String();
}

View file

@ -0,0 +1,90 @@
/**************************************************************************/
/* print_string.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef PRINT_STRING_H
#define PRINT_STRING_H
#include "core/variant/variant.h"
extern void (*_print_func)(String);
typedef void (*PrintHandlerFunc)(void *, const String &p_string, bool p_error, bool p_rich);
struct PrintHandlerList {
PrintHandlerFunc printfunc = nullptr;
void *userdata = nullptr;
PrintHandlerList *next = nullptr;
PrintHandlerList() {}
};
String stringify_variants(const Variant &p_var);
template <typename... Args>
String stringify_variants(const Variant &p_var, Args... p_args) {
return p_var.operator String() + " " + stringify_variants(p_args...);
}
void add_print_handler(PrintHandlerList *p_handler);
void remove_print_handler(const PrintHandlerList *p_handler);
extern void __print_line(const String &p_string);
extern void __print_line_rich(const String &p_string);
extern void print_error(const String &p_string);
extern bool is_print_verbose_enabled();
// This version avoids processing the text to be printed until it actually has to be printed, saving some CPU usage.
#define print_verbose(m_text) \
{ \
if (is_print_verbose_enabled()) { \
print_line(m_text); \
} \
}
inline void print_line(const Variant &v) {
__print_line(stringify_variants(v));
}
inline void print_line_rich(const Variant &v) {
__print_line_rich(stringify_variants(v));
}
template <typename... Args>
void print_line(const Variant &p_var, Args... p_args) {
__print_line(stringify_variants(p_var, p_args...));
}
template <typename... Args>
void print_line_rich(const Variant &p_var, Args... p_args) {
__print_line_rich(stringify_variants(p_var, p_args...));
}
#endif // PRINT_STRING_H

View file

@ -0,0 +1,162 @@
/**************************************************************************/
/* string_buffer.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef STRING_BUFFER_H
#define STRING_BUFFER_H
#include "core/string/ustring.h"
template <int SHORT_BUFFER_SIZE = 64>
class StringBuffer {
char32_t short_buffer[SHORT_BUFFER_SIZE];
String buffer;
int string_length = 0;
_FORCE_INLINE_ char32_t *current_buffer_ptr() {
return static_cast<String &>(buffer).is_empty() ? short_buffer : buffer.ptrw();
}
public:
StringBuffer &append(char32_t p_char);
StringBuffer &append(const String &p_string);
StringBuffer &append(const char *p_str);
StringBuffer &append(const char32_t *p_str, int p_clip_to_len = -1);
_FORCE_INLINE_ void operator+=(char32_t p_char) {
append(p_char);
}
_FORCE_INLINE_ void operator+=(const String &p_string) {
append(p_string);
}
_FORCE_INLINE_ void operator+=(const char *p_str) {
append(p_str);
}
_FORCE_INLINE_ void operator+=(const char32_t *p_str) {
append(p_str);
}
StringBuffer &reserve(int p_size);
int length() const;
String as_string();
double as_double();
int64_t as_int();
_FORCE_INLINE_ operator String() {
return as_string();
}
};
template <int SHORT_BUFFER_SIZE>
StringBuffer<SHORT_BUFFER_SIZE> &StringBuffer<SHORT_BUFFER_SIZE>::append(char32_t p_char) {
reserve(string_length + 2);
current_buffer_ptr()[string_length++] = p_char;
return *this;
}
template <int SHORT_BUFFER_SIZE>
StringBuffer<SHORT_BUFFER_SIZE> &StringBuffer<SHORT_BUFFER_SIZE>::append(const String &p_string) {
return append(p_string.get_data());
}
template <int SHORT_BUFFER_SIZE>
StringBuffer<SHORT_BUFFER_SIZE> &StringBuffer<SHORT_BUFFER_SIZE>::append(const char *p_str) {
int len = strlen(p_str);
reserve(string_length + len + 1);
char32_t *buf = current_buffer_ptr();
for (const char *c_ptr = p_str; *c_ptr; ++c_ptr) {
buf[string_length++] = *c_ptr;
}
return *this;
}
template <int SHORT_BUFFER_SIZE>
StringBuffer<SHORT_BUFFER_SIZE> &StringBuffer<SHORT_BUFFER_SIZE>::append(const char32_t *p_str, int p_clip_to_len) {
int len = 0;
while ((p_clip_to_len < 0 || len < p_clip_to_len) && p_str[len]) {
++len;
}
reserve(string_length + len + 1);
memcpy(&(current_buffer_ptr()[string_length]), p_str, len * sizeof(char32_t));
string_length += len;
return *this;
}
template <int SHORT_BUFFER_SIZE>
StringBuffer<SHORT_BUFFER_SIZE> &StringBuffer<SHORT_BUFFER_SIZE>::reserve(int p_size) {
if (p_size < SHORT_BUFFER_SIZE || p_size < buffer.size()) {
return *this;
}
bool need_copy = string_length > 0 && buffer.is_empty();
buffer.resize(next_power_of_2(p_size));
if (need_copy) {
memcpy(buffer.ptrw(), short_buffer, string_length * sizeof(char32_t));
}
return *this;
}
template <int SHORT_BUFFER_SIZE>
int StringBuffer<SHORT_BUFFER_SIZE>::length() const {
return string_length;
}
template <int SHORT_BUFFER_SIZE>
String StringBuffer<SHORT_BUFFER_SIZE>::as_string() {
current_buffer_ptr()[string_length] = '\0';
if (buffer.is_empty()) {
return String(short_buffer);
} else {
buffer.resize(string_length + 1);
return buffer;
}
}
template <int SHORT_BUFFER_SIZE>
double StringBuffer<SHORT_BUFFER_SIZE>::as_double() {
current_buffer_ptr()[string_length] = '\0';
return String::to_float(current_buffer_ptr());
}
template <int SHORT_BUFFER_SIZE>
int64_t StringBuffer<SHORT_BUFFER_SIZE>::as_int() {
current_buffer_ptr()[string_length] = '\0';
return String::to_int(current_buffer_ptr());
}
#endif // STRING_BUFFER_H

View file

@ -0,0 +1,99 @@
/**************************************************************************/
/* string_builder.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "string_builder.h"
#include <string.h>
StringBuilder &StringBuilder::append(const String &p_string) {
if (p_string.is_empty()) {
return *this;
}
strings.push_back(p_string);
appended_strings.push_back(-1);
string_length += p_string.length();
return *this;
}
StringBuilder &StringBuilder::append(const char *p_cstring) {
int32_t len = strlen(p_cstring);
c_strings.push_back(p_cstring);
appended_strings.push_back(len);
string_length += len;
return *this;
}
String StringBuilder::as_string() const {
if (string_length == 0) {
return "";
}
char32_t *buffer = memnew_arr(char32_t, string_length);
int current_position = 0;
int godot_string_elem = 0;
int c_string_elem = 0;
for (int i = 0; i < appended_strings.size(); i++) {
if (appended_strings[i] == -1) {
// Godot string
const String &s = strings[godot_string_elem];
memcpy(buffer + current_position, s.ptr(), s.length() * sizeof(char32_t));
current_position += s.length();
godot_string_elem++;
} else {
const char *s = c_strings[c_string_elem];
for (int32_t j = 0; j < appended_strings[i]; j++) {
buffer[current_position + j] = s[j];
}
current_position += appended_strings[i];
c_string_elem++;
}
}
String final_string = String(buffer, string_length);
memdelete_arr(buffer);
return final_string;
}

View file

@ -0,0 +1,84 @@
/**************************************************************************/
/* string_builder.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef STRING_BUILDER_H
#define STRING_BUILDER_H
#include "core/string/ustring.h"
#include "core/templates/vector.h"
class StringBuilder {
uint32_t string_length = 0;
Vector<String> strings;
Vector<const char *> c_strings;
// -1 means it's a Godot String
// a natural number means C string.
Vector<int32_t> appended_strings;
public:
StringBuilder &append(const String &p_string);
StringBuilder &append(const char *p_cstring);
_FORCE_INLINE_ StringBuilder &operator+(const String &p_string) {
return append(p_string);
}
_FORCE_INLINE_ StringBuilder &operator+(const char *p_cstring) {
return append(p_cstring);
}
_FORCE_INLINE_ void operator+=(const String &p_string) {
append(p_string);
}
_FORCE_INLINE_ void operator+=(const char *p_cstring) {
append(p_cstring);
}
_FORCE_INLINE_ int num_strings_appended() const {
return appended_strings.size();
}
_FORCE_INLINE_ uint32_t get_string_length() const {
return string_length;
}
String as_string() const;
_FORCE_INLINE_ operator String() const {
return as_string();
}
StringBuilder() {}
};
#endif // STRING_BUILDER_H

View file

@ -0,0 +1,498 @@
/**************************************************************************/
/* string_name.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "string_name.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
StaticCString StaticCString::create(const char *p_ptr) {
StaticCString scs;
scs.ptr = p_ptr;
return scs;
}
StringName::_Data *StringName::_table[STRING_TABLE_LEN];
StringName _scs_create(const char *p_chr, bool p_static) {
return (p_chr[0] ? StringName(StaticCString::create(p_chr), p_static) : StringName());
}
bool StringName::configured = false;
Mutex StringName::mutex;
#ifdef DEBUG_ENABLED
bool StringName::debug_stringname = false;
#endif
void StringName::setup() {
ERR_FAIL_COND(configured);
for (int i = 0; i < STRING_TABLE_LEN; i++) {
_table[i] = nullptr;
}
configured = true;
}
void StringName::cleanup() {
MutexLock lock(mutex);
#ifdef DEBUG_ENABLED
if (unlikely(debug_stringname)) {
Vector<_Data *> data;
for (int i = 0; i < STRING_TABLE_LEN; i++) {
_Data *d = _table[i];
while (d) {
data.push_back(d);
d = d->next;
}
}
print_line("\nStringName reference ranking (from most to least referenced):\n");
data.sort_custom<DebugSortReferences>();
int unreferenced_stringnames = 0;
int rarely_referenced_stringnames = 0;
for (int i = 0; i < data.size(); i++) {
print_line(itos(i + 1) + ": " + data[i]->get_name() + " - " + itos(data[i]->debug_references));
if (data[i]->debug_references == 0) {
unreferenced_stringnames += 1;
} else if (data[i]->debug_references < 5) {
rarely_referenced_stringnames += 1;
}
}
print_line(vformat("\nOut of %d StringNames, %d StringNames were never referenced during this run (0 times) (%.2f%%).", data.size(), unreferenced_stringnames, unreferenced_stringnames / float(data.size()) * 100));
print_line(vformat("Out of %d StringNames, %d StringNames were rarely referenced during this run (1-4 times) (%.2f%%).", data.size(), rarely_referenced_stringnames, rarely_referenced_stringnames / float(data.size()) * 100));
}
#endif
int lost_strings = 0;
for (int i = 0; i < STRING_TABLE_LEN; i++) {
while (_table[i]) {
_Data *d = _table[i];
if (d->static_count.get() != d->refcount.get()) {
lost_strings++;
if (OS::get_singleton()->is_stdout_verbose()) {
String dname = String(d->cname ? d->cname : d->name);
print_line(vformat("Orphan StringName: %s (static: %d, total: %d)", dname, d->static_count.get(), d->refcount.get()));
}
}
_table[i] = _table[i]->next;
memdelete(d);
}
}
if (lost_strings) {
print_verbose(vformat("StringName: %d unclaimed string names at exit.", lost_strings));
}
configured = false;
}
void StringName::unref() {
ERR_FAIL_COND(!configured);
if (_data && _data->refcount.unref()) {
MutexLock lock(mutex);
if (CoreGlobals::leak_reporting_enabled && _data->static_count.get() > 0) {
if (_data->cname) {
ERR_PRINT("BUG: Unreferenced static string to 0: " + String(_data->cname));
} else {
ERR_PRINT("BUG: Unreferenced static string to 0: " + String(_data->name));
}
}
if (_data->prev) {
_data->prev->next = _data->next;
} else {
if (_table[_data->idx] != _data) {
ERR_PRINT("BUG!");
}
_table[_data->idx] = _data->next;
}
if (_data->next) {
_data->next->prev = _data->prev;
}
memdelete(_data);
}
_data = nullptr;
}
bool StringName::operator==(const String &p_name) const {
if (!_data) {
return (p_name.length() == 0);
}
return (_data->get_name() == p_name);
}
bool StringName::operator==(const char *p_name) const {
if (!_data) {
return (p_name[0] == 0);
}
return (_data->get_name() == p_name);
}
bool StringName::operator!=(const String &p_name) const {
return !(operator==(p_name));
}
bool StringName::operator!=(const char *p_name) const {
return !(operator==(p_name));
}
bool StringName::operator!=(const StringName &p_name) const {
// the real magic of all this mess happens here.
// this is why path comparisons are very fast
return _data != p_name._data;
}
void StringName::operator=(const StringName &p_name) {
if (this == &p_name) {
return;
}
unref();
if (p_name._data && p_name._data->refcount.ref()) {
_data = p_name._data;
}
}
StringName::StringName(const StringName &p_name) {
_data = nullptr;
ERR_FAIL_COND(!configured);
if (p_name._data && p_name._data->refcount.ref()) {
_data = p_name._data;
}
}
void StringName::assign_static_unique_class_name(StringName *ptr, const char *p_name) {
mutex.lock();
if (*ptr == StringName()) {
*ptr = StringName(p_name, true);
}
mutex.unlock();
}
StringName::StringName(const char *p_name, bool p_static) {
_data = nullptr;
ERR_FAIL_COND(!configured);
if (!p_name || p_name[0] == 0) {
return; //empty, ignore
}
MutexLock lock(mutex);
uint32_t hash = String::hash(p_name);
uint32_t idx = hash & STRING_TABLE_MASK;
_data = _table[idx];
while (_data) {
// compare hash first
if (_data->hash == hash && _data->get_name() == p_name) {
break;
}
_data = _data->next;
}
if (_data && _data->refcount.ref()) {
// exists
if (p_static) {
_data->static_count.increment();
}
#ifdef DEBUG_ENABLED
if (unlikely(debug_stringname)) {
_data->debug_references++;
}
#endif
return;
}
_data = memnew(_Data);
_data->name = p_name;
_data->refcount.init();
_data->static_count.set(p_static ? 1 : 0);
_data->hash = hash;
_data->idx = idx;
_data->cname = nullptr;
_data->next = _table[idx];
_data->prev = nullptr;
#ifdef DEBUG_ENABLED
if (unlikely(debug_stringname)) {
// Keep in memory, force static.
_data->refcount.ref();
_data->static_count.increment();
}
#endif
if (_table[idx]) {
_table[idx]->prev = _data;
}
_table[idx] = _data;
}
StringName::StringName(const StaticCString &p_static_string, bool p_static) {
_data = nullptr;
ERR_FAIL_COND(!configured);
ERR_FAIL_COND(!p_static_string.ptr || !p_static_string.ptr[0]);
MutexLock lock(mutex);
uint32_t hash = String::hash(p_static_string.ptr);
uint32_t idx = hash & STRING_TABLE_MASK;
_data = _table[idx];
while (_data) {
// compare hash first
if (_data->hash == hash && _data->get_name() == p_static_string.ptr) {
break;
}
_data = _data->next;
}
if (_data && _data->refcount.ref()) {
// exists
if (p_static) {
_data->static_count.increment();
}
#ifdef DEBUG_ENABLED
if (unlikely(debug_stringname)) {
_data->debug_references++;
}
#endif
return;
}
_data = memnew(_Data);
_data->refcount.init();
_data->static_count.set(p_static ? 1 : 0);
_data->hash = hash;
_data->idx = idx;
_data->cname = p_static_string.ptr;
_data->next = _table[idx];
_data->prev = nullptr;
#ifdef DEBUG_ENABLED
if (unlikely(debug_stringname)) {
// Keep in memory, force static.
_data->refcount.ref();
_data->static_count.increment();
}
#endif
if (_table[idx]) {
_table[idx]->prev = _data;
}
_table[idx] = _data;
}
StringName::StringName(const String &p_name, bool p_static) {
_data = nullptr;
ERR_FAIL_COND(!configured);
if (p_name.is_empty()) {
return;
}
MutexLock lock(mutex);
uint32_t hash = p_name.hash();
uint32_t idx = hash & STRING_TABLE_MASK;
_data = _table[idx];
while (_data) {
if (_data->hash == hash && _data->get_name() == p_name) {
break;
}
_data = _data->next;
}
if (_data && _data->refcount.ref()) {
// exists
if (p_static) {
_data->static_count.increment();
}
#ifdef DEBUG_ENABLED
if (unlikely(debug_stringname)) {
_data->debug_references++;
}
#endif
return;
}
_data = memnew(_Data);
_data->name = p_name;
_data->refcount.init();
_data->static_count.set(p_static ? 1 : 0);
_data->hash = hash;
_data->idx = idx;
_data->cname = nullptr;
_data->next = _table[idx];
_data->prev = nullptr;
#ifdef DEBUG_ENABLED
if (unlikely(debug_stringname)) {
// Keep in memory, force static.
_data->refcount.ref();
_data->static_count.increment();
}
#endif
if (_table[idx]) {
_table[idx]->prev = _data;
}
_table[idx] = _data;
}
StringName StringName::search(const char *p_name) {
ERR_FAIL_COND_V(!configured, StringName());
ERR_FAIL_NULL_V(p_name, StringName());
if (!p_name[0]) {
return StringName();
}
MutexLock lock(mutex);
uint32_t hash = String::hash(p_name);
uint32_t idx = hash & STRING_TABLE_MASK;
_Data *_data = _table[idx];
while (_data) {
// compare hash first
if (_data->hash == hash && _data->get_name() == p_name) {
break;
}
_data = _data->next;
}
if (_data && _data->refcount.ref()) {
#ifdef DEBUG_ENABLED
if (unlikely(debug_stringname)) {
_data->debug_references++;
}
#endif
return StringName(_data);
}
return StringName(); //does not exist
}
StringName StringName::search(const char32_t *p_name) {
ERR_FAIL_COND_V(!configured, StringName());
ERR_FAIL_NULL_V(p_name, StringName());
if (!p_name[0]) {
return StringName();
}
MutexLock lock(mutex);
uint32_t hash = String::hash(p_name);
uint32_t idx = hash & STRING_TABLE_MASK;
_Data *_data = _table[idx];
while (_data) {
// compare hash first
if (_data->hash == hash && _data->get_name() == p_name) {
break;
}
_data = _data->next;
}
if (_data && _data->refcount.ref()) {
return StringName(_data);
}
return StringName(); //does not exist
}
StringName StringName::search(const String &p_name) {
ERR_FAIL_COND_V(p_name.is_empty(), StringName());
MutexLock lock(mutex);
uint32_t hash = p_name.hash();
uint32_t idx = hash & STRING_TABLE_MASK;
_Data *_data = _table[idx];
while (_data) {
// compare hash first
if (_data->hash == hash && p_name == _data->get_name()) {
break;
}
_data = _data->next;
}
if (_data && _data->refcount.ref()) {
#ifdef DEBUG_ENABLED
if (unlikely(debug_stringname)) {
_data->debug_references++;
}
#endif
return StringName(_data);
}
return StringName(); //does not exist
}
bool operator==(const String &p_name, const StringName &p_string_name) {
return p_name == p_string_name.operator String();
}
bool operator!=(const String &p_name, const StringName &p_string_name) {
return p_name != p_string_name.operator String();
}
bool operator==(const char *p_name, const StringName &p_string_name) {
return p_name == p_string_name.operator String();
}
bool operator!=(const char *p_name, const StringName &p_string_name) {
return p_name != p_string_name.operator String();
}

View file

@ -0,0 +1,218 @@
/**************************************************************************/
/* string_name.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef STRING_NAME_H
#define STRING_NAME_H
#include "core/os/mutex.h"
#include "core/string/ustring.h"
#include "core/templates/safe_refcount.h"
#define UNIQUE_NODE_PREFIX "%"
class Main;
struct StaticCString {
const char *ptr;
static StaticCString create(const char *p_ptr);
};
class StringName {
enum {
STRING_TABLE_BITS = 16,
STRING_TABLE_LEN = 1 << STRING_TABLE_BITS,
STRING_TABLE_MASK = STRING_TABLE_LEN - 1
};
struct _Data {
SafeRefCount refcount;
SafeNumeric<uint32_t> static_count;
const char *cname = nullptr;
String name;
#ifdef DEBUG_ENABLED
uint32_t debug_references = 0;
#endif
String get_name() const { return cname ? String(cname) : name; }
int idx = 0;
uint32_t hash = 0;
_Data *prev = nullptr;
_Data *next = nullptr;
_Data() {}
};
static _Data *_table[STRING_TABLE_LEN];
_Data *_data = nullptr;
void unref();
friend void register_core_types();
friend void unregister_core_types();
friend class Main;
static Mutex mutex;
static void setup();
static void cleanup();
static bool configured;
#ifdef DEBUG_ENABLED
struct DebugSortReferences {
bool operator()(const _Data *p_left, const _Data *p_right) const {
return p_left->debug_references > p_right->debug_references;
}
};
static bool debug_stringname;
#endif
StringName(_Data *p_data) { _data = p_data; }
public:
operator const void *() const { return (_data && (_data->cname || !_data->name.is_empty())) ? (void *)1 : nullptr; }
bool operator==(const String &p_name) const;
bool operator==(const char *p_name) const;
bool operator!=(const String &p_name) const;
bool operator!=(const char *p_name) const;
_FORCE_INLINE_ bool is_node_unique_name() const {
if (!_data) {
return false;
}
if (_data->cname != nullptr) {
return (char32_t)_data->cname[0] == (char32_t)UNIQUE_NODE_PREFIX[0];
} else {
return (char32_t)_data->name[0] == (char32_t)UNIQUE_NODE_PREFIX[0];
}
}
_FORCE_INLINE_ bool operator<(const StringName &p_name) const {
return _data < p_name._data;
}
_FORCE_INLINE_ bool operator<=(const StringName &p_name) const {
return _data <= p_name._data;
}
_FORCE_INLINE_ bool operator>(const StringName &p_name) const {
return _data > p_name._data;
}
_FORCE_INLINE_ bool operator>=(const StringName &p_name) const {
return _data >= p_name._data;
}
_FORCE_INLINE_ bool operator==(const StringName &p_name) const {
// the real magic of all this mess happens here.
// this is why path comparisons are very fast
return _data == p_name._data;
}
_FORCE_INLINE_ uint32_t hash() const {
if (_data) {
return _data->hash;
} else {
return 0;
}
}
_FORCE_INLINE_ const void *data_unique_pointer() const {
return (void *)_data;
}
bool operator!=(const StringName &p_name) const;
_FORCE_INLINE_ operator String() const {
if (_data) {
if (_data->cname) {
return String(_data->cname);
} else {
return _data->name;
}
}
return String();
}
static StringName search(const char *p_name);
static StringName search(const char32_t *p_name);
static StringName search(const String &p_name);
struct AlphCompare {
_FORCE_INLINE_ bool operator()(const StringName &l, const StringName &r) const {
const char *l_cname = l._data ? l._data->cname : "";
const char *r_cname = r._data ? r._data->cname : "";
if (l_cname) {
if (r_cname) {
return is_str_less(l_cname, r_cname);
} else {
return is_str_less(l_cname, r._data->name.ptr());
}
} else {
if (r_cname) {
return is_str_less(l._data->name.ptr(), r_cname);
} else {
return is_str_less(l._data->name.ptr(), r._data->name.ptr());
}
}
}
};
void operator=(const StringName &p_name);
StringName(const char *p_name, bool p_static = false);
StringName(const StringName &p_name);
StringName(const String &p_name, bool p_static = false);
StringName(const StaticCString &p_static_string, bool p_static = false);
StringName() {}
static void assign_static_unique_class_name(StringName *ptr, const char *p_name);
_FORCE_INLINE_ ~StringName() {
if (likely(configured) && _data) { //only free if configured
unref();
}
}
#ifdef DEBUG_ENABLED
static void set_debug_stringnames(bool p_enable) { debug_stringname = p_enable; }
#endif
};
bool operator==(const String &p_name, const StringName &p_string_name);
bool operator!=(const String &p_name, const StringName &p_string_name);
bool operator==(const char *p_name, const StringName &p_string_name);
bool operator!=(const char *p_name, const StringName &p_string_name);
StringName _scs_create(const char *p_chr, bool p_static = false);
/*
* The SNAME macro is used to speed up StringName creation, as it allows caching it after the first usage in a very efficient way.
* It should NOT be used everywhere, but instead in places where high performance is required and the creation of a StringName
* can be costly. Places where it should be used are:
* - Control::get_theme_*(<name> and Window::get_theme_*(<name> functions.
* - emit_signal(<name>,..) function
* - call_deferred(<name>,..) function
* - Comparisons to a StringName in overridden _set and _get methods.
*
* Use in places that can be called hundreds of times per frame (or more) is recommended, but this situation is very rare. If in doubt, do not use.
*/
#define SNAME(m_arg) ([]() -> const StringName & { static StringName sname = _scs_create(m_arg, true); return sname; })()
#endif // STRING_NAME_H

View file

@ -0,0 +1,46 @@
/**************************************************************************/
/* translation.compat.inc */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DISABLE_DEPRECATED
void Translation::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("add_message", "src_message", "xlated_message", "context"), &Translation::add_message, DEFVAL(""));
ClassDB::bind_compatibility_method(D_METHOD("add_plural_message", "src_message", "xlated_messages", "context"), &Translation::add_plural_message, DEFVAL(""));
ClassDB::bind_compatibility_method(D_METHOD("get_message", "src_message", "context"), &Translation::get_message, DEFVAL(""));
ClassDB::bind_compatibility_method(D_METHOD("get_plural_message", "src_message", "src_plural_message", "n", "context"), &Translation::get_plural_message, DEFVAL(""));
ClassDB::bind_compatibility_method(D_METHOD("erase_message", "src_message", "context"), &Translation::erase_message, DEFVAL(""));
}
void TranslationServer::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL(""));
ClassDB::bind_compatibility_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL(""));
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,205 @@
/**************************************************************************/
/* translation.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TRANSLATION_H
#define TRANSLATION_H
#include "core/io/resource.h"
#include "core/object/gdvirtual.gen.inc"
class Translation : public Resource {
GDCLASS(Translation, Resource);
OBJ_SAVE_TYPE(Translation);
RES_BASE_EXTENSION("translation");
String locale = "en";
HashMap<StringName, StringName> translation_map;
virtual Vector<String> _get_message_list() const;
virtual Dictionary _get_messages() const;
virtual void _set_messages(const Dictionary &p_messages);
void _notify_translation_changed_if_applies();
protected:
static void _bind_methods();
#ifndef DISABLE_DEPRECATED
static void _bind_compatibility_methods();
#endif
GDVIRTUAL2RC(StringName, _get_message, StringName, StringName);
GDVIRTUAL4RC(StringName, _get_plural_message, StringName, StringName, int, StringName);
public:
void set_locale(const String &p_locale);
_FORCE_INLINE_ String get_locale() const { return locale; }
virtual void add_message(const StringName &p_src_text, const StringName &p_xlated_text, const StringName &p_context = "");
virtual void add_plural_message(const StringName &p_src_text, const Vector<String> &p_plural_xlated_texts, const StringName &p_context = "");
virtual StringName get_message(const StringName &p_src_text, const StringName &p_context = "") const; //overridable for other implementations
virtual StringName get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context = "") const;
virtual void erase_message(const StringName &p_src_text, const StringName &p_context = "");
virtual void get_message_list(List<StringName> *r_messages) const;
virtual int get_message_count() const;
virtual Vector<String> get_translated_message_list() const;
Translation() {}
};
class TranslationServer : public Object {
GDCLASS(TranslationServer, Object);
String locale = "en";
String fallback;
HashSet<Ref<Translation>> translations;
Ref<Translation> tool_translation;
Ref<Translation> property_translation;
Ref<Translation> doc_translation;
Ref<Translation> extractable_translation;
bool enabled = true;
bool pseudolocalization_enabled = false;
bool pseudolocalization_accents_enabled = false;
bool pseudolocalization_double_vowels_enabled = false;
bool pseudolocalization_fake_bidi_enabled = false;
bool pseudolocalization_override_enabled = false;
bool pseudolocalization_skip_placeholders_enabled = false;
float expansion_ratio = 0.0;
String pseudolocalization_prefix;
String pseudolocalization_suffix;
StringName tool_pseudolocalize(const StringName &p_message) const;
String get_override_string(String &p_message) const;
String double_vowels(String &p_message) const;
String replace_with_accented_string(String &p_message) const;
String wrap_with_fakebidi_characters(String &p_message) const;
String add_padding(const String &p_message, int p_length) const;
const char32_t *get_accented_version(char32_t p_character) const;
bool is_placeholder(String &p_message, int p_index) const;
static TranslationServer *singleton;
bool _load_translations(const String &p_from);
String _standardize_locale(const String &p_locale, bool p_add_defaults) const;
StringName _get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural = "", int p_n = 0) const;
static void _bind_methods();
#ifndef DISABLE_DEPRECATED
static void _bind_compatibility_methods();
#endif
struct LocaleScriptInfo {
String name;
String script;
String default_country;
HashSet<String> supported_countries;
};
static Vector<LocaleScriptInfo> locale_script_info;
static HashMap<String, String> language_map;
static HashMap<String, String> script_map;
static HashMap<String, String> locale_rename_map;
static HashMap<String, String> country_name_map;
static HashMap<String, String> country_rename_map;
static HashMap<String, String> variant_map;
void init_locale_info();
public:
_FORCE_INLINE_ static TranslationServer *get_singleton() { return singleton; }
void set_enabled(bool p_enabled) { enabled = p_enabled; }
_FORCE_INLINE_ bool is_enabled() const { return enabled; }
void set_locale(const String &p_locale);
String get_locale() const;
Ref<Translation> get_translation_object(const String &p_locale);
Vector<String> get_all_languages() const;
String get_language_name(const String &p_language) const;
Vector<String> get_all_scripts() const;
String get_script_name(const String &p_script) const;
Vector<String> get_all_countries() const;
String get_country_name(const String &p_country) const;
String get_locale_name(const String &p_locale) const;
PackedStringArray get_loaded_locales() const;
void add_translation(const Ref<Translation> &p_translation);
void remove_translation(const Ref<Translation> &p_translation);
StringName translate(const StringName &p_message, const StringName &p_context = "") const;
StringName translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
StringName pseudolocalize(const StringName &p_message) const;
bool is_pseudolocalization_enabled() const;
void set_pseudolocalization_enabled(bool p_enabled);
void reload_pseudolocalization();
String standardize_locale(const String &p_locale) const;
int compare_locales(const String &p_locale_a, const String &p_locale_b) const;
String get_tool_locale();
void set_tool_translation(const Ref<Translation> &p_translation);
Ref<Translation> get_tool_translation() const;
StringName tool_translate(const StringName &p_message, const StringName &p_context = "") const;
StringName tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
void set_property_translation(const Ref<Translation> &p_translation);
StringName property_translate(const StringName &p_message, const StringName &p_context = "") const;
void set_doc_translation(const Ref<Translation> &p_translation);
StringName doc_translate(const StringName &p_message, const StringName &p_context = "") const;
StringName doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
void set_extractable_translation(const Ref<Translation> &p_translation);
StringName extractable_translate(const StringName &p_message, const StringName &p_context = "") const;
StringName extractable_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
void setup();
void clear();
void load_translations();
#ifdef TOOLS_ENABLED
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
#endif // TOOLS_ENABLED
TranslationServer();
};
#endif // TRANSLATION_H

View file

@ -0,0 +1,350 @@
/**************************************************************************/
/* translation_po.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "translation_po.h"
#include "core/io/file_access.h"
#ifdef DEBUG_TRANSLATION_PO
void TranslationPO::print_translation_map() {
Error err;
Ref<FileAccess> file = FileAccess::open("translation_map_print_test.txt", FileAccess::WRITE, &err);
if (err != OK) {
ERR_PRINT("Failed to open translation_map_print_test.txt");
return;
}
file->store_line("NPlural : " + String::num_int64(get_plural_forms()));
file->store_line("Plural rule : " + get_plural_rule());
file->store_line("");
List<StringName> context_l;
translation_map.get_key_list(&context_l);
for (const StringName &ctx : context_l) {
file->store_line(" ===== Context: " + String::utf8(String(ctx).utf8()) + " ===== ");
const HashMap<StringName, Vector<StringName>> &inner_map = translation_map[ctx];
List<StringName> id_l;
inner_map.get_key_list(&id_l);
for (List<StringName>::Element *E2 = id_l.front(); E2; E2 = E2->next()) {
StringName id = E2->get();
file->store_line("msgid: " + String::utf8(String(id).utf8()));
for (int i = 0; i < inner_map[id].size(); i++) {
file->store_line("msgstr[" + String::num_int64(i) + "]: " + String::utf8(String(inner_map[id][i]).utf8()));
}
file->store_line("");
}
}
}
#endif
Dictionary TranslationPO::_get_messages() const {
// Return translation_map as a Dictionary.
Dictionary d;
for (const KeyValue<StringName, HashMap<StringName, Vector<StringName>>> &E : translation_map) {
Dictionary d2;
for (const KeyValue<StringName, Vector<StringName>> &E2 : E.value) {
d2[E2.key] = E2.value;
}
d[E.key] = d2;
}
return d;
}
void TranslationPO::_set_messages(const Dictionary &p_messages) {
// Construct translation_map from a Dictionary.
List<Variant> context_l;
p_messages.get_key_list(&context_l);
for (const Variant &ctx : context_l) {
const Dictionary &id_str_map = p_messages[ctx];
HashMap<StringName, Vector<StringName>> temp_map;
List<Variant> id_l;
id_str_map.get_key_list(&id_l);
for (List<Variant>::Element *E2 = id_l.front(); E2; E2 = E2->next()) {
StringName id = E2->get();
temp_map[id] = id_str_map[id];
}
translation_map[ctx] = temp_map;
}
}
Vector<String> TranslationPO::get_translated_message_list() const {
Vector<String> msgs;
for (const KeyValue<StringName, HashMap<StringName, Vector<StringName>>> &E : translation_map) {
if (E.key != StringName()) {
continue;
}
for (const KeyValue<StringName, Vector<StringName>> &E2 : E.value) {
for (const StringName &E3 : E2.value) {
msgs.push_back(E3);
}
}
}
return msgs;
}
Vector<String> TranslationPO::_get_message_list() const {
// Return all keys in translation_map.
List<StringName> msgs;
get_message_list(&msgs);
Vector<String> v;
for (const StringName &E : msgs) {
v.push_back(E);
}
return v;
}
int TranslationPO::_get_plural_index(int p_n) const {
// Get a number between [0;number of plural forms).
input_val.clear();
input_val.push_back(p_n);
return _eq_test(equi_tests, 0);
}
int TranslationPO::_eq_test(const Ref<EQNode> &p_node, const Variant &p_result) const {
if (p_node.is_valid()) {
Error err = expr->parse(p_node->regex, input_name);
ERR_FAIL_COND_V_MSG(err != OK, 0, vformat("Cannot parse expression \"%s\". Error: %s", p_node->regex, expr->get_error_text()));
Variant result = expr->execute(input_val);
ERR_FAIL_COND_V_MSG(expr->has_execute_failed(), 0, vformat("Cannot evaluate expression \"%s\".", p_node->regex));
if (bool(result)) {
return _eq_test(p_node->left, result);
} else {
return _eq_test(p_node->right, result);
}
} else {
return p_result;
}
}
int TranslationPO::_find_unquoted(const String &p_src, char32_t p_chr) const {
const int len = p_src.length();
if (len == 0) {
return -1;
}
const char32_t *src = p_src.get_data();
bool in_quote = false;
for (int i = 0; i < len; i++) {
if (in_quote) {
if (src[i] == ')') {
in_quote = false;
}
} else {
if (src[i] == '(') {
in_quote = true;
} else if (src[i] == p_chr) {
return i;
}
}
}
return -1;
}
void TranslationPO::_cache_plural_tests(const String &p_plural_rule, Ref<EQNode> &p_node) {
// Some examples of p_plural_rule passed in can have the form:
// "n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5" (Arabic)
// "n >= 2" (French) // When evaluating the last, especially careful with this one.
// "n != 1" (English)
String rule = p_plural_rule;
if (rule.begins_with("(") && rule.ends_with(")")) {
int bcount = 0;
for (int i = 1; i < rule.length() - 1 && bcount >= 0; i++) {
if (rule[i] == '(') {
bcount++;
} else if (rule[i] == ')') {
bcount--;
}
}
if (bcount == 0) {
rule = rule.substr(1, rule.length() - 2);
}
}
int first_ques_mark = _find_unquoted(rule, '?');
int first_colon = _find_unquoted(rule, ':');
if (first_ques_mark == -1) {
p_node->regex = rule.strip_edges();
return;
}
p_node->regex = rule.substr(0, first_ques_mark).strip_edges();
p_node->left.instantiate();
_cache_plural_tests(rule.substr(first_ques_mark + 1, first_colon - first_ques_mark - 1).strip_edges(), p_node->left);
p_node->right.instantiate();
_cache_plural_tests(rule.substr(first_colon + 1).strip_edges(), p_node->right);
}
void TranslationPO::set_plural_rule(const String &p_plural_rule) {
// Set plural_forms and plural_rule.
// p_plural_rule passed in has the form "Plural-Forms: nplurals=2; plural=(n >= 2);".
int first_semi_col = p_plural_rule.find(";");
plural_forms = p_plural_rule.substr(p_plural_rule.find("=") + 1, first_semi_col - (p_plural_rule.find("=") + 1)).to_int();
int expression_start = p_plural_rule.find("=", first_semi_col) + 1;
int second_semi_col = p_plural_rule.rfind(";");
plural_rule = p_plural_rule.substr(expression_start, second_semi_col - expression_start).strip_edges();
// Setup the cache to make evaluating plural rule faster later on.
equi_tests.instantiate();
_cache_plural_tests(plural_rule, equi_tests);
expr.instantiate();
input_name.push_back("n");
}
void TranslationPO::add_message(const StringName &p_src_text, const StringName &p_xlated_text, const StringName &p_context) {
HashMap<StringName, Vector<StringName>> &map_id_str = translation_map[p_context];
if (map_id_str.has(p_src_text)) {
WARN_PRINT("Double translations for \"" + String(p_src_text) + "\" under the same context \"" + String(p_context) + "\" for locale \"" + get_locale() + "\".\nThere should only be one unique translation for a given string under the same context.");
map_id_str[p_src_text].set(0, p_xlated_text);
} else {
map_id_str[p_src_text].push_back(p_xlated_text);
}
}
void TranslationPO::add_plural_message(const StringName &p_src_text, const Vector<String> &p_plural_xlated_texts, const StringName &p_context) {
ERR_FAIL_COND_MSG(p_plural_xlated_texts.size() != plural_forms, "Trying to add plural texts that don't match the required number of plural forms for locale \"" + get_locale() + "\"");
HashMap<StringName, Vector<StringName>> &map_id_str = translation_map[p_context];
if (map_id_str.has(p_src_text)) {
WARN_PRINT("Double translations for \"" + p_src_text + "\" under the same context \"" + p_context + "\" for locale " + get_locale() + ".\nThere should only be one unique translation for a given string under the same context.");
map_id_str[p_src_text].clear();
}
for (int i = 0; i < p_plural_xlated_texts.size(); i++) {
map_id_str[p_src_text].push_back(p_plural_xlated_texts[i]);
}
}
int TranslationPO::get_plural_forms() const {
return plural_forms;
}
String TranslationPO::get_plural_rule() const {
return plural_rule;
}
StringName TranslationPO::get_message(const StringName &p_src_text, const StringName &p_context) const {
if (!translation_map.has(p_context) || !translation_map[p_context].has(p_src_text)) {
return StringName();
}
ERR_FAIL_COND_V_MSG(translation_map[p_context][p_src_text].is_empty(), StringName(), "Source text \"" + String(p_src_text) + "\" is registered but doesn't have a translation. Please report this bug.");
return translation_map[p_context][p_src_text][0];
}
StringName TranslationPO::get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context) const {
ERR_FAIL_COND_V_MSG(p_n < 0, StringName(), "N passed into translation to get a plural message should not be negative. For negative numbers, use singular translation please. Search \"gettext PO Plural Forms\" online for the documentation on translating negative numbers.");
// If the query is the same as last time, return the cached result.
if (p_n == last_plural_n && p_context == last_plural_context && p_src_text == last_plural_key) {
return translation_map[p_context][p_src_text][last_plural_mapped_index];
}
if (!translation_map.has(p_context) || !translation_map[p_context].has(p_src_text)) {
return StringName();
}
ERR_FAIL_COND_V_MSG(translation_map[p_context][p_src_text].is_empty(), StringName(), "Source text \"" + String(p_src_text) + "\" is registered but doesn't have a translation. Please report this bug.");
int plural_index = _get_plural_index(p_n);
ERR_FAIL_COND_V_MSG(plural_index < 0 || translation_map[p_context][p_src_text].size() < plural_index + 1, StringName(), "Plural index returned or number of plural translations is not valid. Please report this bug.");
// Cache result so that if the next entry is the same, we can return directly.
// _get_plural_index(p_n) can get very costly, especially when evaluating long plural-rule (Arabic)
last_plural_key = p_src_text;
last_plural_context = p_context;
last_plural_n = p_n;
last_plural_mapped_index = plural_index;
return translation_map[p_context][p_src_text][plural_index];
}
void TranslationPO::erase_message(const StringName &p_src_text, const StringName &p_context) {
if (!translation_map.has(p_context)) {
return;
}
translation_map[p_context].erase(p_src_text);
}
void TranslationPO::get_message_list(List<StringName> *r_messages) const {
// OptimizedTranslation uses this function to get the list of msgid.
// Return all the keys of translation_map under "" context.
for (const KeyValue<StringName, HashMap<StringName, Vector<StringName>>> &E : translation_map) {
if (E.key != StringName()) {
continue;
}
for (const KeyValue<StringName, Vector<StringName>> &E2 : E.value) {
r_messages->push_back(E2.key);
}
}
}
int TranslationPO::get_message_count() const {
int count = 0;
for (const KeyValue<StringName, HashMap<StringName, Vector<StringName>>> &E : translation_map) {
count += E.value.size();
}
return count;
}
void TranslationPO::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_plural_forms"), &TranslationPO::get_plural_forms);
ClassDB::bind_method(D_METHOD("get_plural_rule"), &TranslationPO::get_plural_rule);
}

View file

@ -0,0 +1,103 @@
/**************************************************************************/
/* translation_po.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TRANSLATION_PO_H
#define TRANSLATION_PO_H
//#define DEBUG_TRANSLATION_PO
#include "core/math/expression.h"
#include "core/string/translation.h"
class TranslationPO : public Translation {
GDCLASS(TranslationPO, Translation);
// TLDR: Maps context to a list of source strings and translated strings. In PO terms, maps msgctxt to a list of msgid and msgstr.
// The first key corresponds to context, and the second key (of the contained HashMap) corresponds to source string.
// The value Vector<StringName> in the second map stores the translated strings. Index 0, 1, 2 matches msgstr[0], msgstr[1], msgstr[2]... in the case of plurals.
// Otherwise index 0 matches to msgstr in a singular translation.
// Strings without context have "" as first key.
HashMap<StringName, HashMap<StringName, Vector<StringName>>> translation_map;
int plural_forms = 0; // 0 means no "Plural-Forms" is given in the PO header file. The min for all languages is 1.
String plural_rule;
// Cache temporary variables related to _get_plural_index() to make it faster
class EQNode : public RefCounted {
public:
String regex;
Ref<EQNode> left;
Ref<EQNode> right;
};
Ref<EQNode> equi_tests;
int _find_unquoted(const String &p_src, char32_t p_chr) const;
int _eq_test(const Ref<EQNode> &p_node, const Variant &p_result) const;
Vector<String> input_name;
mutable Ref<Expression> expr;
mutable Array input_val;
mutable StringName last_plural_key;
mutable StringName last_plural_context;
mutable int last_plural_n = -1; // Set it to an impossible value at the beginning.
mutable int last_plural_mapped_index = 0;
void _cache_plural_tests(const String &p_plural_rule, Ref<EQNode> &p_node);
int _get_plural_index(int p_n) const;
Vector<String> _get_message_list() const override;
Dictionary _get_messages() const override;
void _set_messages(const Dictionary &p_messages) override;
protected:
static void _bind_methods();
public:
Vector<String> get_translated_message_list() const override;
void get_message_list(List<StringName> *r_messages) const override;
int get_message_count() const override;
void add_message(const StringName &p_src_text, const StringName &p_xlated_text, const StringName &p_context = "") override;
void add_plural_message(const StringName &p_src_text, const Vector<String> &p_plural_xlated_texts, const StringName &p_context = "") override;
StringName get_message(const StringName &p_src_text, const StringName &p_context = "") const override;
StringName get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context = "") const override;
void erase_message(const StringName &p_src_text, const StringName &p_context = "") override;
void set_plural_rule(const String &p_plural_rule);
int get_plural_forms() const;
String get_plural_rule() const;
#ifdef DEBUG_TRANSLATION_PO
void print_translation_map();
#endif
TranslationPO() {}
};
#endif // TRANSLATION_PO_H

1415
engine/core/string/ucaps.h Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,644 @@
/**************************************************************************/
/* ustring.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef USTRING_GODOT_H
#define USTRING_GODOT_H
// Note: _GODOT suffix added to header guard to avoid conflict with ICU header.
#include "core/string/char_utils.h"
#include "core/templates/cowdata.h"
#include "core/templates/vector.h"
#include "core/typedefs.h"
#include "core/variant/array.h"
/*************************************************************************/
/* CharProxy */
/*************************************************************************/
template <typename T>
class CharProxy {
friend class Char16String;
friend class CharString;
friend class String;
const int _index;
CowData<T> &_cowdata;
static const T _null = 0;
_FORCE_INLINE_ CharProxy(const int &p_index, CowData<T> &p_cowdata) :
_index(p_index),
_cowdata(p_cowdata) {}
public:
_FORCE_INLINE_ CharProxy(const CharProxy<T> &p_other) :
_index(p_other._index),
_cowdata(p_other._cowdata) {}
_FORCE_INLINE_ operator T() const {
if (unlikely(_index == _cowdata.size())) {
return _null;
}
return _cowdata.get(_index);
}
_FORCE_INLINE_ const T *operator&() const {
return _cowdata.ptr() + _index;
}
_FORCE_INLINE_ void operator=(const T &p_other) const {
_cowdata.set(_index, p_other);
}
_FORCE_INLINE_ void operator=(const CharProxy<T> &p_other) const {
_cowdata.set(_index, p_other.operator T());
}
};
/*************************************************************************/
/* Char16String */
/*************************************************************************/
class Char16String {
CowData<char16_t> _cowdata;
static const char16_t _null;
public:
_FORCE_INLINE_ char16_t *ptrw() { return _cowdata.ptrw(); }
_FORCE_INLINE_ const char16_t *ptr() const { return _cowdata.ptr(); }
_FORCE_INLINE_ int size() const { return _cowdata.size(); }
Error resize(int p_size) { return _cowdata.resize(p_size); }
_FORCE_INLINE_ char16_t get(int p_index) const { return _cowdata.get(p_index); }
_FORCE_INLINE_ void set(int p_index, const char16_t &p_elem) { _cowdata.set(p_index, p_elem); }
_FORCE_INLINE_ const char16_t &operator[](int p_index) const {
if (unlikely(p_index == _cowdata.size())) {
return _null;
}
return _cowdata.get(p_index);
}
_FORCE_INLINE_ CharProxy<char16_t> operator[](int p_index) { return CharProxy<char16_t>(p_index, _cowdata); }
_FORCE_INLINE_ Char16String() {}
_FORCE_INLINE_ Char16String(const Char16String &p_str) { _cowdata._ref(p_str._cowdata); }
_FORCE_INLINE_ void operator=(const Char16String &p_str) { _cowdata._ref(p_str._cowdata); }
_FORCE_INLINE_ Char16String(const char16_t *p_cstr) { copy_from(p_cstr); }
void operator=(const char16_t *p_cstr);
bool operator<(const Char16String &p_right) const;
Char16String &operator+=(char16_t p_char);
int length() const { return size() ? size() - 1 : 0; }
const char16_t *get_data() const;
operator const char16_t *() const { return get_data(); };
protected:
void copy_from(const char16_t *p_cstr);
};
/*************************************************************************/
/* CharString */
/*************************************************************************/
class CharString {
CowData<char> _cowdata;
static const char _null;
public:
_FORCE_INLINE_ char *ptrw() { return _cowdata.ptrw(); }
_FORCE_INLINE_ const char *ptr() const { return _cowdata.ptr(); }
_FORCE_INLINE_ int size() const { return _cowdata.size(); }
Error resize(int p_size) { return _cowdata.resize(p_size); }
_FORCE_INLINE_ char get(int p_index) const { return _cowdata.get(p_index); }
_FORCE_INLINE_ void set(int p_index, const char &p_elem) { _cowdata.set(p_index, p_elem); }
_FORCE_INLINE_ const char &operator[](int p_index) const {
if (unlikely(p_index == _cowdata.size())) {
return _null;
}
return _cowdata.get(p_index);
}
_FORCE_INLINE_ CharProxy<char> operator[](int p_index) { return CharProxy<char>(p_index, _cowdata); }
_FORCE_INLINE_ CharString() {}
_FORCE_INLINE_ CharString(const CharString &p_str) { _cowdata._ref(p_str._cowdata); }
_FORCE_INLINE_ void operator=(const CharString &p_str) { _cowdata._ref(p_str._cowdata); }
_FORCE_INLINE_ CharString(const char *p_cstr) { copy_from(p_cstr); }
void operator=(const char *p_cstr);
bool operator<(const CharString &p_right) const;
bool operator==(const CharString &p_right) const;
CharString &operator+=(char p_char);
int length() const { return size() ? size() - 1 : 0; }
const char *get_data() const;
operator const char *() const { return get_data(); };
protected:
void copy_from(const char *p_cstr);
};
/*************************************************************************/
/* String */
/*************************************************************************/
struct StrRange {
const char32_t *c_str;
int len;
StrRange(const char32_t *p_c_str = nullptr, int p_len = 0) {
c_str = p_c_str;
len = p_len;
}
};
class String {
CowData<char32_t> _cowdata;
static const char32_t _null;
static const char32_t _replacement_char;
void copy_from(const char *p_cstr);
void copy_from(const char *p_cstr, const int p_clip_to);
void copy_from(const wchar_t *p_cstr);
void copy_from(const wchar_t *p_cstr, const int p_clip_to);
void copy_from(const char32_t *p_cstr);
void copy_from(const char32_t *p_cstr, const int p_clip_to);
void copy_from(const char32_t &p_char);
void copy_from_unchecked(const char32_t *p_char, const int p_length);
bool _base_is_subsequence_of(const String &p_string, bool case_insensitive) const;
int _count(const String &p_string, int p_from, int p_to, bool p_case_insensitive) const;
int _count(const char *p_string, int p_from, int p_to, bool p_case_insensitive) const;
String _camelcase_to_underscore() const;
public:
enum {
npos = -1 ///<for "some" compatibility with std::string (npos is a huge value in std::string)
};
_FORCE_INLINE_ char32_t *ptrw() { return _cowdata.ptrw(); }
_FORCE_INLINE_ const char32_t *ptr() const { return _cowdata.ptr(); }
void remove_at(int p_index) { _cowdata.remove_at(p_index); }
_FORCE_INLINE_ void clear() { resize(0); }
_FORCE_INLINE_ char32_t get(int p_index) const { return _cowdata.get(p_index); }
_FORCE_INLINE_ void set(int p_index, const char32_t &p_elem) { _cowdata.set(p_index, p_elem); }
_FORCE_INLINE_ int size() const { return _cowdata.size(); }
Error resize(int p_size) { return _cowdata.resize(p_size); }
_FORCE_INLINE_ const char32_t &operator[](int p_index) const {
if (unlikely(p_index == _cowdata.size())) {
return _null;
}
return _cowdata.get(p_index);
}
_FORCE_INLINE_ CharProxy<char32_t> operator[](int p_index) { return CharProxy<char32_t>(p_index, _cowdata); }
bool operator==(const String &p_str) const;
bool operator!=(const String &p_str) const;
String operator+(const String &p_str) const;
String operator+(char32_t p_char) const;
String &operator+=(const String &);
String &operator+=(char32_t p_char);
String &operator+=(const char *p_str);
String &operator+=(const wchar_t *p_str);
String &operator+=(const char32_t *p_str);
/* Compatibility Operators */
void operator=(const char *p_str);
void operator=(const wchar_t *p_str);
void operator=(const char32_t *p_str);
bool operator==(const char *p_str) const;
bool operator==(const wchar_t *p_str) const;
bool operator==(const char32_t *p_str) const;
bool operator==(const StrRange &p_str_range) const;
bool operator!=(const char *p_str) const;
bool operator!=(const wchar_t *p_str) const;
bool operator!=(const char32_t *p_str) const;
bool operator<(const char32_t *p_str) const;
bool operator<(const char *p_str) const;
bool operator<(const wchar_t *p_str) const;
bool operator<(const String &p_str) const;
bool operator<=(const String &p_str) const;
bool operator>(const String &p_str) const;
bool operator>=(const String &p_str) const;
signed char casecmp_to(const String &p_str) const;
signed char nocasecmp_to(const String &p_str) const;
signed char naturalcasecmp_to(const String &p_str) const;
signed char naturalnocasecmp_to(const String &p_str) const;
// Special sorting for file names. Names starting with `_` are put before all others except those starting with `.`, otherwise natural comparison is used.
signed char filecasecmp_to(const String &p_str) const;
signed char filenocasecmp_to(const String &p_str) const;
const char32_t *get_data() const;
/* standard size stuff */
_FORCE_INLINE_ int length() const {
int s = size();
return s ? (s - 1) : 0; // length does not include zero
}
bool is_valid_string() const;
/* debug, error messages */
void print_unicode_error(const String &p_message, bool p_critical = false) const;
/* complex helpers */
String substr(int p_from, int p_chars = -1) const;
int find(const String &p_str, int p_from = 0) const; ///< return <0 if failed
int find(const char *p_str, int p_from = 0) const; ///< return <0 if failed
int find_char(const char32_t &p_char, int p_from = 0) const; ///< return <0 if failed
int findn(const String &p_str, int p_from = 0) const; ///< return <0 if failed, case insensitive
int findn(const char *p_str, int p_from = 0) const; ///< return <0 if failed
int rfind(const String &p_str, int p_from = -1) const; ///< return <0 if failed
int rfind(const char *p_str, int p_from = -1) const; ///< return <0 if failed
int rfindn(const String &p_str, int p_from = -1) const; ///< return <0 if failed, case insensitive
int rfindn(const char *p_str, int p_from = -1) const; ///< return <0 if failed
int findmk(const Vector<String> &p_keys, int p_from = 0, int *r_key = nullptr) const; ///< return <0 if failed
bool match(const String &p_wildcard) const;
bool matchn(const String &p_wildcard) const;
bool begins_with(const String &p_string) const;
bool begins_with(const char *p_string) const;
bool ends_with(const String &p_string) const;
bool ends_with(const char *p_string) const;
bool is_enclosed_in(const String &p_string) const;
bool is_subsequence_of(const String &p_string) const;
bool is_subsequence_ofn(const String &p_string) const;
bool is_quoted() const;
Vector<String> bigrams() const;
float similarity(const String &p_string) const;
String format(const Variant &values, const String &placeholder = "{_}") const;
String replace_first(const String &p_key, const String &p_with) const;
String replace_first(const char *p_key, const char *p_with) const;
String replace(const String &p_key, const String &p_with) const;
String replace(const char *p_key, const char *p_with) const;
String replacen(const String &p_key, const String &p_with) const;
String replacen(const char *p_key, const char *p_with) const;
String repeat(int p_count) const;
String reverse() const;
String insert(int p_at_pos, const String &p_string) const;
String erase(int p_pos, int p_chars = 1) const;
String pad_decimals(int p_digits) const;
String pad_zeros(int p_digits) const;
String trim_prefix(const String &p_prefix) const;
String trim_prefix(const char *p_prefix) const;
String trim_suffix(const String &p_suffix) const;
String trim_suffix(const char *p_suffix) const;
String lpad(int min_length, const String &character = " ") const;
String rpad(int min_length, const String &character = " ") const;
String sprintf(const Array &values, bool *error) const;
String quote(const String &quotechar = "\"") const;
String unquote() const;
static String num(double p_num, int p_decimals = -1);
static String num_scientific(double p_num);
static String num_real(double p_num, bool p_trailing = true);
static String num_int64(int64_t p_num, int base = 10, bool capitalize_hex = false);
static String num_uint64(uint64_t p_num, int base = 10, bool capitalize_hex = false);
static String chr(char32_t p_char);
static String md5(const uint8_t *p_md5);
static String hex_encode_buffer(const uint8_t *p_buffer, int p_len);
Vector<uint8_t> hex_decode() const;
bool is_numeric() const;
double to_float() const;
int64_t hex_to_int() const;
int64_t bin_to_int() const;
int64_t to_int() const;
static int64_t to_int(const char *p_str, int p_len = -1);
static int64_t to_int(const wchar_t *p_str, int p_len = -1);
static int64_t to_int(const char32_t *p_str, int p_len = -1, bool p_clamp = false);
static double to_float(const char *p_str);
static double to_float(const wchar_t *p_str, const wchar_t **r_end = nullptr);
static double to_float(const char32_t *p_str, const char32_t **r_end = nullptr);
static uint32_t num_characters(int64_t p_int);
String capitalize() const;
String to_camel_case() const;
String to_pascal_case() const;
String to_snake_case() const;
String get_with_code_lines() const;
int get_slice_count(const String &p_splitter) const;
int get_slice_count(const char *p_splitter) const;
String get_slice(const String &p_splitter, int p_slice) const;
String get_slice(const char *p_splitter, int p_slice) const;
String get_slicec(char32_t p_splitter, int p_slice) const;
Vector<String> split(const String &p_splitter = "", bool p_allow_empty = true, int p_maxsplit = 0) const;
Vector<String> split(const char *p_splitter = "", bool p_allow_empty = true, int p_maxsplit = 0) const;
Vector<String> rsplit(const String &p_splitter = "", bool p_allow_empty = true, int p_maxsplit = 0) const;
Vector<String> rsplit(const char *p_splitter = "", bool p_allow_empty = true, int p_maxsplit = 0) const;
Vector<String> split_spaces() const;
Vector<double> split_floats(const String &p_splitter, bool p_allow_empty = true) const;
Vector<float> split_floats_mk(const Vector<String> &p_splitters, bool p_allow_empty = true) const;
Vector<int> split_ints(const String &p_splitter, bool p_allow_empty = true) const;
Vector<int> split_ints_mk(const Vector<String> &p_splitters, bool p_allow_empty = true) const;
String join(const Vector<String> &parts) const;
static char32_t char_uppercase(char32_t p_char);
static char32_t char_lowercase(char32_t p_char);
String to_upper() const;
String to_lower() const;
int count(const String &p_string, int p_from = 0, int p_to = 0) const;
int count(const char *p_string, int p_from = 0, int p_to = 0) const;
int countn(const String &p_string, int p_from = 0, int p_to = 0) const;
int countn(const char *p_string, int p_from = 0, int p_to = 0) const;
String left(int p_len) const;
String right(int p_len) const;
String indent(const String &p_prefix) const;
String dedent() const;
String strip_edges(bool left = true, bool right = true) const;
String strip_escapes() const;
String lstrip(const String &p_chars) const;
String rstrip(const String &p_chars) const;
String get_extension() const;
String get_basename() const;
String path_join(const String &p_file) const;
char32_t unicode_at(int p_idx) const;
CharString ascii(bool p_allow_extended = false) const;
CharString utf8() const;
Error parse_utf8(const char *p_utf8, int p_len = -1, bool p_skip_cr = false);
static String utf8(const char *p_utf8, int p_len = -1);
Char16String utf16() const;
Error parse_utf16(const char16_t *p_utf16, int p_len = -1, bool p_default_little_endian = true);
static String utf16(const char16_t *p_utf16, int p_len = -1);
static uint32_t hash(const char32_t *p_cstr, int p_len); /* hash the string */
static uint32_t hash(const char32_t *p_cstr); /* hash the string */
static uint32_t hash(const wchar_t *p_cstr, int p_len); /* hash the string */
static uint32_t hash(const wchar_t *p_cstr); /* hash the string */
static uint32_t hash(const char *p_cstr, int p_len); /* hash the string */
static uint32_t hash(const char *p_cstr); /* hash the string */
uint32_t hash() const; /* hash the string */
uint64_t hash64() const; /* hash the string */
String md5_text() const;
String sha1_text() const;
String sha256_text() const;
Vector<uint8_t> md5_buffer() const;
Vector<uint8_t> sha1_buffer() const;
Vector<uint8_t> sha256_buffer() const;
_FORCE_INLINE_ bool is_empty() const { return length() == 0; }
_FORCE_INLINE_ bool contains(const char *p_str) const { return find(p_str) != -1; }
_FORCE_INLINE_ bool contains(const String &p_str) const { return find(p_str) != -1; }
_FORCE_INLINE_ bool containsn(const char *p_str) const { return findn(p_str) != -1; }
_FORCE_INLINE_ bool containsn(const String &p_str) const { return findn(p_str) != -1; }
// path functions
bool is_absolute_path() const;
bool is_relative_path() const;
bool is_resource_file() const;
String path_to(const String &p_path) const;
String path_to_file(const String &p_path) const;
String get_base_dir() const;
String get_file() const;
static String humanize_size(uint64_t p_size);
String simplify_path() const;
bool is_network_share_path() const;
String xml_escape(bool p_escape_quotes = false) const;
String xml_unescape() const;
String uri_encode() const;
String uri_decode() const;
String c_escape() const;
String c_escape_multiline() const;
String c_unescape() const;
String json_escape() const;
Error parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path) const;
String property_name_encode() const;
// node functions
static String get_invalid_node_name_characters(bool p_allow_internal = false);
String validate_node_name() const;
String validate_identifier() const;
String validate_filename() const;
bool is_valid_identifier() const;
bool is_valid_int() const;
bool is_valid_float() const;
bool is_valid_hex_number(bool p_with_prefix) const;
bool is_valid_html_color() const;
bool is_valid_ip_address() const;
bool is_valid_filename() const;
/**
* The constructors must not depend on other overloads
*/
_FORCE_INLINE_ String() {}
_FORCE_INLINE_ String(const String &p_str) { _cowdata._ref(p_str._cowdata); }
_FORCE_INLINE_ void operator=(const String &p_str) { _cowdata._ref(p_str._cowdata); }
Vector<uint8_t> to_ascii_buffer() const;
Vector<uint8_t> to_utf8_buffer() const;
Vector<uint8_t> to_utf16_buffer() const;
Vector<uint8_t> to_utf32_buffer() const;
Vector<uint8_t> to_wchar_buffer() const;
String(const char *p_str);
String(const wchar_t *p_str);
String(const char32_t *p_str);
String(const char *p_str, int p_clip_to_len);
String(const wchar_t *p_str, int p_clip_to_len);
String(const char32_t *p_str, int p_clip_to_len);
String(const StrRange &p_range);
};
bool operator==(const char *p_chr, const String &p_str);
bool operator==(const wchar_t *p_chr, const String &p_str);
bool operator!=(const char *p_chr, const String &p_str);
bool operator!=(const wchar_t *p_chr, const String &p_str);
String operator+(const char *p_chr, const String &p_str);
String operator+(const wchar_t *p_chr, const String &p_str);
String operator+(char32_t p_chr, const String &p_str);
String itos(int64_t p_val);
String uitos(uint64_t p_val);
String rtos(double p_val);
String rtoss(double p_val); //scientific version
struct NoCaseComparator {
bool operator()(const String &p_a, const String &p_b) const {
return p_a.nocasecmp_to(p_b) < 0;
}
};
struct NaturalNoCaseComparator {
bool operator()(const String &p_a, const String &p_b) const {
return p_a.naturalnocasecmp_to(p_b) < 0;
}
};
struct FileNoCaseComparator {
bool operator()(const String &p_a, const String &p_b) const {
return p_a.filenocasecmp_to(p_b) < 0;
}
};
template <typename L, typename R>
_FORCE_INLINE_ bool is_str_less(const L *l_ptr, const R *r_ptr) {
while (true) {
const char32_t l = *l_ptr;
const char32_t r = *r_ptr;
if (l == 0 && r == 0) {
return false;
} else if (l == 0) {
return true;
} else if (r == 0) {
return false;
} else if (l < r) {
return true;
} else if (l > r) {
return false;
}
l_ptr++;
r_ptr++;
}
}
/* end of namespace */
// Tool translate (TTR and variants) for the editor UI,
// and doc translate for the class reference (DTR).
#ifdef TOOLS_ENABLED
// Gets parsed.
String TTR(const String &p_text, const String &p_context = "");
String TTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context = "");
String DTR(const String &p_text, const String &p_context = "");
String DTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context = "");
// Use for C strings.
#define TTRC(m_value) (m_value)
// Use to avoid parsing (for use later with C strings).
#define TTRGET(m_value) TTR(m_value)
#else
#define TTRC(m_value) (m_value)
#define TTRGET(m_value) (m_value)
#endif
// Use this to mark property names for editor translation.
// Often for dynamic properties defined in _get_property_list().
// Property names defined directly inside EDITOR_DEF, GLOBAL_DEF, and ADD_PROPERTY macros don't need this.
#define PNAME(m_value) (m_value)
// Similar to PNAME, but to mark groups, i.e. properties with PROPERTY_USAGE_GROUP.
// Groups defined directly inside ADD_GROUP macros don't need this.
// The arguments are the same as ADD_GROUP. m_prefix is only used for extraction.
#define GNAME(m_value, m_prefix) (m_value)
// Runtime translate for the public node API.
String RTR(const String &p_text, const String &p_context = "");
String RTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context = "");
/**
* "Extractable TRanslate". Used for strings that can appear inside an exported
* project (such as the ones in nodes like `FileDialog`), which are made possible
* to add in the POT generator. A translation context can optionally be specified
* to disambiguate between identical source strings in translations.
* When placeholders are desired, use vformat(ETR("Example: %s"), some_string)`.
* If a string mentions a quantity (and may therefore need a dynamic plural form),
* use `ETRN()` instead of `ETR()`.
*
* NOTE: This function is for string extraction only, and will just return the
* string it was given. The translation itself should be done internally by nodes
* with `atr()` instead.
*/
_FORCE_INLINE_ String ETR(const String &p_text, const String &p_context = "") {
return p_text;
}
/**
* "Extractable TRanslate for N items". Used for strings that can appear inside an
* exported project (such as the ones in nodes like `FileDialog`), which are made
* possible to add in the POT generator. A translation context can optionally be
* specified to disambiguate between identical source strings in translations.
* Use `ETR()` if the string doesn't need dynamic plural form. When placeholders
* are desired, use `vformat(ETRN("%d item", "%d items", some_integer), some_integer)`.
* The placeholder must be present in both strings to avoid run-time warnings in `vformat()`.
*
* NOTE: This function is for string extraction only, and will just return the
* string it was given. The translation itself should be done internally by nodes
* with `atr()` instead.
*/
_FORCE_INLINE_ String ETRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context = "") {
if (p_n == 1) {
return p_text;
}
return p_text_plural;
}
bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end);
_FORCE_INLINE_ void sarray_add_str(Vector<String> &arr) {
}
_FORCE_INLINE_ void sarray_add_str(Vector<String> &arr, const String &p_str) {
arr.push_back(p_str);
}
template <typename... P>
_FORCE_INLINE_ void sarray_add_str(Vector<String> &arr, const String &p_str, P... p_args) {
arr.push_back(p_str);
sarray_add_str(arr, p_args...);
}
template <typename... P>
_FORCE_INLINE_ Vector<String> sarray(P... p_args) {
Vector<String> arr;
sarray_add_str(arr, p_args...);
return arr;
}
#endif // USTRING_GODOT_H