Reorganized core/ directory, it was too fatty already
-Removed FuncRef, since Callable makes it obsolete -Removed int_types.h as its obsolete in c++11+ -Changed color names code
This commit is contained in:
parent
30b6db99a9
commit
127458ed17
811 changed files with 2232 additions and 2488 deletions
7
core/string/SCsub
Normal file
7
core/string/SCsub
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
Import("env")
|
||||
|
||||
env_string = env.Clone()
|
||||
|
||||
env_string.add_source_files(env.core_sources, "*.cpp")
|
||||
288
core/string/compressed_translation.cpp
Normal file
288
core/string/compressed_translation.cpp
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
/*************************************************************************/
|
||||
/* compressed_translation.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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 "compressed_translation.h"
|
||||
|
||||
#include "core/templates/pair.h"
|
||||
|
||||
extern "C" {
|
||||
#include "thirdparty/misc/smaz.h"
|
||||
}
|
||||
|
||||
struct _PHashTranslationCmp {
|
||||
int orig_len;
|
||||
CharString compressed;
|
||||
int offset;
|
||||
};
|
||||
|
||||
void PHashTranslation::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
|
||||
List<StringName> keys;
|
||||
p_from->get_message_list(&keys);
|
||||
|
||||
int size = Math::larger_prime(keys.size());
|
||||
|
||||
Vector<Vector<Pair<int, CharString>>> buckets;
|
||||
Vector<Map<uint32_t, int>> table;
|
||||
Vector<uint32_t> hfunc_table;
|
||||
Vector<_PHashTranslationCmp> compressed;
|
||||
|
||||
table.resize(size);
|
||||
hfunc_table.resize(size);
|
||||
buckets.resize(size);
|
||||
compressed.resize(keys.size());
|
||||
|
||||
int idx = 0;
|
||||
int total_compression_size = 0;
|
||||
int total_string_size = 0;
|
||||
|
||||
for (List<StringName>::Element *E = keys.front(); E; E = E->next()) {
|
||||
//hash string
|
||||
CharString cs = E->get().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->get()).operator String().utf8();
|
||||
_PHashTranslationCmp 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();
|
||||
total_string_size += src_s.size();
|
||||
idx++;
|
||||
}
|
||||
|
||||
int bucket_table_size = 0;
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
const Vector<Pair<int, CharString>> &b = buckets[i];
|
||||
Map<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;
|
||||
int collisions = 0;
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
const Map<uint32_t, int> &t = table[i];
|
||||
if (t.size() == 0) {
|
||||
htw[i] = 0xFFFFFFFF; //nothing
|
||||
continue;
|
||||
} else if (t.size() > 1) {
|
||||
collisions += t.size() - 1;
|
||||
}
|
||||
|
||||
htw[i] = btindex;
|
||||
btw[btindex++] = t.size();
|
||||
btw[btindex++] = hfunc_table[i];
|
||||
|
||||
for (Map<uint32_t, int>::Element *E = t.front(); E; E = E->next()) {
|
||||
btw[btindex++] = E->key();
|
||||
btw[btindex++] = compressed[E->get()].offset;
|
||||
btw[btindex++] = compressed[E->get()].compressed.size();
|
||||
btw[btindex++] = compressed[E->get()].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 PHashTranslation::_set(const StringName &p_name, const Variant &p_value) {
|
||||
String name = p_name.operator String();
|
||||
if (name == "hash_table") {
|
||||
hash_table = p_value;
|
||||
} else if (name == "bucket_table") {
|
||||
bucket_table = p_value;
|
||||
} else if (name == "strings") {
|
||||
strings = p_value;
|
||||
} else if (name == "load_from") {
|
||||
generate(p_value);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PHashTranslation::_get(const StringName &p_name, Variant &r_ret) const {
|
||||
String name = p_name.operator String();
|
||||
if (name == "hash_table") {
|
||||
r_ret = hash_table;
|
||||
} else if (name == "bucket_table") {
|
||||
r_ret = bucket_table;
|
||||
} else if (name == "strings") {
|
||||
r_ret = strings;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
StringName PHashTranslation::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 PHashTranslation.
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
StringName PHashTranslation::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 PHashTranslation.
|
||||
return get_message(p_src_text, p_context);
|
||||
}
|
||||
|
||||
void PHashTranslation::_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 PHashTranslation::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("generate", "from"), &PHashTranslation::generate);
|
||||
}
|
||||
89
core/string/compressed_translation.h
Normal file
89
core/string/compressed_translation.h
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/*************************************************************************/
|
||||
/* compressed_translation.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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 COMPRESSED_TRANSLATION_H
|
||||
#define COMPRESSED_TRANSLATION_H
|
||||
|
||||
#include "core/string/translation.h"
|
||||
|
||||
class PHashTranslation : public Translation {
|
||||
GDCLASS(PHashTranslation, 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 extreemly 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;
|
||||
void generate(const Ref<Translation> &p_from);
|
||||
|
||||
PHashTranslation() {}
|
||||
};
|
||||
|
||||
#endif // COMPRESSED_TRANSLATION_H
|
||||
429
core/string/node_path.cpp
Normal file
429
core/string/node_path.cpp
Normal file
|
|
@ -0,0 +1,429 @@
|
|||
/*************************************************************************/
|
||||
/* node_path.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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_COND_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_COND_V(!data, StringName());
|
||||
ERR_FAIL_INDEX_V(p_idx, data->subpath.size(), StringName());
|
||||
return data->subpath[p_idx];
|
||||
}
|
||||
|
||||
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_subnames() const {
|
||||
ERR_FAIL_COND_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::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;
|
||||
|
||||
for (int i = src_dirs.size() - 1; i > common_parent; i--) {
|
||||
relpath.push_back("..");
|
||||
}
|
||||
|
||||
for (int i = common_parent + 1; i < dst_dirs.size(); i++) {
|
||||
relpath.push_back(dst_dirs[i]);
|
||||
}
|
||||
|
||||
if (relpath.size() == 0) {
|
||||
relpath.push_back(".");
|
||||
}
|
||||
|
||||
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(i);
|
||||
i--;
|
||||
} else if (data->path[i].operator String() == ".." && i > 0 && data->path[i - 1].operator String() != "." && data->path[i - 1].operator String() != "..") {
|
||||
//remove both
|
||||
data->path.remove(i - 1);
|
||||
data->path.remove(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) {
|
||||
return;
|
||||
}
|
||||
|
||||
data = memnew(Data);
|
||||
data->refcount.init();
|
||||
data->absolute = p_absolute;
|
||||
data->path = p_path;
|
||||
data->has_slashes = true;
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
data = memnew(Data);
|
||||
data->refcount.init();
|
||||
data->absolute = p_absolute;
|
||||
data->path = p_path;
|
||||
data->subpath = p_subpath;
|
||||
data->has_slashes = true;
|
||||
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;
|
||||
bool has_slashes = false;
|
||||
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 == "") {
|
||||
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;
|
||||
has_slashes = 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->has_slashes = has_slashes;
|
||||
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();
|
||||
}
|
||||
99
core/string/node_path.h
Normal file
99
core/string/node_path.h
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/*************************************************************************/
|
||||
/* node_path.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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_subpath;
|
||||
bool absolute;
|
||||
bool has_slashes;
|
||||
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;
|
||||
Vector<StringName> get_names() const;
|
||||
Vector<StringName> get_subnames() const;
|
||||
StringName get_concatenated_subnames() const;
|
||||
|
||||
NodePath rel_path_to(const NodePath &p_np) const;
|
||||
NodePath get_as_property_path() const;
|
||||
|
||||
void prepend_period();
|
||||
|
||||
NodePath get_parent() const;
|
||||
|
||||
_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
|
||||
110
core/string/print_string.cpp
Normal file
110
core/string/print_string.cpp
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/*************************************************************************/
|
||||
/* print_string.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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/os/os.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static PrintHandlerList *print_handler_list = nullptr;
|
||||
bool _print_line_enabled = true;
|
||||
bool _print_error_enabled = true;
|
||||
|
||||
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(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_COND(l == nullptr);
|
||||
}
|
||||
|
||||
void print_line(String p_string) {
|
||||
if (!_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);
|
||||
l = l->next;
|
||||
}
|
||||
|
||||
_global_unlock();
|
||||
}
|
||||
|
||||
void print_error(String p_string) {
|
||||
if (!_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);
|
||||
l = l->next;
|
||||
}
|
||||
|
||||
_global_unlock();
|
||||
}
|
||||
|
||||
void print_verbose(String p_string) {
|
||||
if (OS::get_singleton()->is_stdout_verbose()) {
|
||||
print_line(p_string);
|
||||
}
|
||||
}
|
||||
58
core/string/print_string.h
Normal file
58
core/string/print_string.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/*************************************************************************/
|
||||
/* print_string.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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/string/ustring.h"
|
||||
|
||||
extern void (*_print_func)(String);
|
||||
|
||||
typedef void (*PrintHandlerFunc)(void *, const String &p_string, bool p_error);
|
||||
|
||||
struct PrintHandlerList {
|
||||
PrintHandlerFunc printfunc = nullptr;
|
||||
void *userdata = nullptr;
|
||||
|
||||
PrintHandlerList *next = nullptr;
|
||||
|
||||
PrintHandlerList() {}
|
||||
};
|
||||
|
||||
void add_print_handler(PrintHandlerList *p_handler);
|
||||
void remove_print_handler(PrintHandlerList *p_handler);
|
||||
|
||||
extern bool _print_line_enabled;
|
||||
extern bool _print_error_enabled;
|
||||
extern void print_line(String p_string);
|
||||
extern void print_error(String p_string);
|
||||
extern void print_verbose(String p_string);
|
||||
|
||||
#endif // PRINT_STRING_H
|
||||
162
core/string/string_buffer.h
Normal file
162
core/string/string_buffer.h
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/*************************************************************************/
|
||||
/* string_buffer.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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).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.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.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
|
||||
99
core/string/string_builder.cpp
Normal file
99
core/string/string_builder.cpp
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/*************************************************************************/
|
||||
/* string_builder.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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 == String()) {
|
||||
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;
|
||||
}
|
||||
84
core/string/string_builder.h
Normal file
84
core/string/string_builder.h
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/*************************************************************************/
|
||||
/* string_builder.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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
|
||||
393
core/string/string_name.cpp
Normal file
393
core/string/string_name.cpp
Normal file
|
|
@ -0,0 +1,393 @@
|
|||
/*************************************************************************/
|
||||
/* string_name.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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) {
|
||||
return (p_chr[0] ? StringName(StaticCString::create(p_chr)) : StringName());
|
||||
}
|
||||
|
||||
bool StringName::configured = false;
|
||||
Mutex StringName::mutex;
|
||||
|
||||
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);
|
||||
|
||||
int lost_strings = 0;
|
||||
for (int i = 0; i < STRING_TABLE_LEN; i++) {
|
||||
while (_table[i]) {
|
||||
_Data *d = _table[i];
|
||||
lost_strings++;
|
||||
if (OS::get_singleton()->is_stdout_verbose()) {
|
||||
if (d->cname) {
|
||||
print_line("Orphan StringName: " + String(d->cname));
|
||||
} else {
|
||||
print_line("Orphan StringName: " + String(d->name));
|
||||
}
|
||||
}
|
||||
|
||||
_table[i] = _table[i]->next;
|
||||
memdelete(d);
|
||||
}
|
||||
}
|
||||
if (lost_strings) {
|
||||
print_verbose("StringName: " + itos(lost_strings) + " unclaimed string names at exit.");
|
||||
}
|
||||
}
|
||||
|
||||
void StringName::unref() {
|
||||
ERR_FAIL_COND(!configured);
|
||||
|
||||
if (_data && _data->refcount.unref()) {
|
||||
MutexLock lock(mutex);
|
||||
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
|
||||
StringName::StringName(const char *p_name) {
|
||||
_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) {
|
||||
if (_data->refcount.ref()) {
|
||||
// exists
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_data = memnew(_Data);
|
||||
_data->name = p_name;
|
||||
_data->refcount.init();
|
||||
_data->hash = hash;
|
||||
_data->idx = idx;
|
||||
_data->cname = nullptr;
|
||||
_data->next = _table[idx];
|
||||
_data->prev = nullptr;
|
||||
if (_table[idx]) {
|
||||
_table[idx]->prev = _data;
|
||||
}
|
||||
_table[idx] = _data;
|
||||
}
|
||||
|
||||
StringName::StringName(const StaticCString &p_static_string) {
|
||||
_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) {
|
||||
if (_data->refcount.ref()) {
|
||||
// exists
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_data = memnew(_Data);
|
||||
|
||||
_data->refcount.init();
|
||||
_data->hash = hash;
|
||||
_data->idx = idx;
|
||||
_data->cname = p_static_string.ptr;
|
||||
_data->next = _table[idx];
|
||||
_data->prev = nullptr;
|
||||
if (_table[idx]) {
|
||||
_table[idx]->prev = _data;
|
||||
}
|
||||
_table[idx] = _data;
|
||||
}
|
||||
|
||||
StringName::StringName(const String &p_name) {
|
||||
_data = nullptr;
|
||||
|
||||
ERR_FAIL_COND(!configured);
|
||||
|
||||
if (p_name == String()) {
|
||||
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) {
|
||||
if (_data->refcount.ref()) {
|
||||
// exists
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_data = memnew(_Data);
|
||||
_data->name = p_name;
|
||||
_data->refcount.init();
|
||||
_data->hash = hash;
|
||||
_data->idx = idx;
|
||||
_data->cname = nullptr;
|
||||
_data->next = _table[idx];
|
||||
_data->prev = nullptr;
|
||||
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_COND_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 char32_t *p_name) {
|
||||
ERR_FAIL_COND_V(!configured, StringName());
|
||||
|
||||
ERR_FAIL_COND_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 == "", 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()) {
|
||||
return StringName(_data);
|
||||
}
|
||||
|
||||
return StringName(); //does not exist
|
||||
}
|
||||
|
||||
StringName::~StringName() {
|
||||
unref();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
165
core/string/string_name.h
Normal file
165
core/string/string_name.h
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/*************************************************************************/
|
||||
/* string_name.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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"
|
||||
|
||||
class Main;
|
||||
|
||||
struct StaticCString {
|
||||
const char *ptr;
|
||||
static StaticCString create(const char *p_ptr);
|
||||
};
|
||||
|
||||
class StringName {
|
||||
enum {
|
||||
|
||||
STRING_TABLE_BITS = 12,
|
||||
STRING_TABLE_LEN = 1 << STRING_TABLE_BITS,
|
||||
STRING_TABLE_MASK = STRING_TABLE_LEN - 1
|
||||
};
|
||||
|
||||
struct _Data {
|
||||
SafeRefCount refcount;
|
||||
const char *cname = nullptr;
|
||||
String name;
|
||||
|
||||
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;
|
||||
|
||||
union _HashUnion {
|
||||
_Data *ptr;
|
||||
uint32_t hash;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
StringName(_Data *p_data) { _data = p_data; }
|
||||
|
||||
public:
|
||||
operator const void *() const { return (_data && (_data->cname || !_data->name.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;
|
||||
_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);
|
||||
StringName(const StringName &p_name);
|
||||
StringName(const String &p_name);
|
||||
StringName(const StaticCString &p_static_string);
|
||||
StringName() {}
|
||||
~StringName();
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
#endif // STRING_NAME_H
|
||||
1303
core/string/translation.cpp
Normal file
1303
core/string/translation.cpp
Normal file
File diff suppressed because it is too large
Load diff
129
core/string/translation.h
Normal file
129
core/string/translation.h
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
/*************************************************************************/
|
||||
/* translation.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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"
|
||||
|
||||
class Translation : public Resource {
|
||||
GDCLASS(Translation, Resource);
|
||||
OBJ_SAVE_TYPE(Translation);
|
||||
RES_BASE_EXTENSION("translation");
|
||||
|
||||
String locale = "en";
|
||||
Map<StringName, StringName> translation_map;
|
||||
|
||||
virtual Vector<String> _get_message_list() const;
|
||||
virtual Dictionary _get_messages() const;
|
||||
virtual void _set_messages(const Dictionary &p_messages);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
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;
|
||||
|
||||
Translation() {}
|
||||
};
|
||||
|
||||
class TranslationServer : public Object {
|
||||
GDCLASS(TranslationServer, Object);
|
||||
|
||||
String locale = "en";
|
||||
String fallback;
|
||||
|
||||
Set<Ref<Translation>> translations;
|
||||
Ref<Translation> tool_translation;
|
||||
Ref<Translation> doc_translation;
|
||||
|
||||
Map<String, String> locale_name_map;
|
||||
|
||||
bool enabled = true;
|
||||
|
||||
static TranslationServer *singleton;
|
||||
bool _load_translations(const String &p_from);
|
||||
|
||||
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();
|
||||
|
||||
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);
|
||||
|
||||
String get_locale_name(const String &p_locale) const;
|
||||
|
||||
Array 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;
|
||||
|
||||
static Vector<String> get_all_locales();
|
||||
static Vector<String> get_all_locale_names();
|
||||
static bool is_locale_valid(const String &p_locale);
|
||||
static String standardize_locale(const String &p_locale);
|
||||
static String get_language_code(const String &p_locale);
|
||||
|
||||
void set_tool_translation(const Ref<Translation> &p_translation);
|
||||
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_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 setup();
|
||||
|
||||
void clear();
|
||||
|
||||
void load_translations();
|
||||
|
||||
TranslationServer();
|
||||
};
|
||||
|
||||
#endif // TRANSLATION_H
|
||||
312
core/string/translation_po.cpp
Normal file
312
core/string/translation_po.cpp
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
/*************************************************************************/
|
||||
/* translation_po.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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/os/file_access.h"
|
||||
|
||||
#ifdef DEBUG_TRANSLATION_PO
|
||||
void TranslationPO::print_translation_map() {
|
||||
Error err;
|
||||
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(this->get_plural_forms()));
|
||||
file->store_line("Plural rule : " + this->get_plural_rule());
|
||||
file->store_line("");
|
||||
|
||||
List<StringName> context_l;
|
||||
translation_map.get_key_list(&context_l);
|
||||
for (auto E = context_l.front(); E; E = E->next()) {
|
||||
StringName ctx = E->get();
|
||||
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 (auto 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("");
|
||||
}
|
||||
}
|
||||
file->close();
|
||||
}
|
||||
#endif
|
||||
|
||||
Dictionary TranslationPO::_get_messages() const {
|
||||
// Return translation_map as a Dictionary.
|
||||
|
||||
Dictionary d;
|
||||
|
||||
List<StringName> context_l;
|
||||
translation_map.get_key_list(&context_l);
|
||||
for (auto E = context_l.front(); E; E = E->next()) {
|
||||
StringName ctx = E->get();
|
||||
const HashMap<StringName, Vector<StringName>> &id_str_map = translation_map[ctx];
|
||||
|
||||
Dictionary d2;
|
||||
List<StringName> id_l;
|
||||
id_str_map.get_key_list(&id_l);
|
||||
// Save list of id and strs associated with a context in a temporary dictionary.
|
||||
for (auto E2 = id_l.front(); E2; E2 = E2->next()) {
|
||||
StringName id = E2->get();
|
||||
d2[id] = id_str_map[id];
|
||||
}
|
||||
|
||||
d[ctx] = 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 (auto E = context_l.front(); E; E = E->next()) {
|
||||
StringName ctx = E->get();
|
||||
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 (auto 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_message_list() const {
|
||||
// Return all keys in translation_map.
|
||||
|
||||
List<StringName> msgs;
|
||||
get_message_list(&msgs);
|
||||
|
||||
Vector<String> v;
|
||||
for (auto E = msgs.front(); E; E = E->next()) {
|
||||
v.push_back(E->get());
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
Variant result;
|
||||
for (int i = 0; i < equi_tests.size(); i++) {
|
||||
Error err = expr->parse(equi_tests[i], input_name);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, 0, "Cannot parse expression. Error: " + expr->get_error_text());
|
||||
|
||||
result = expr->execute(input_val);
|
||||
ERR_FAIL_COND_V_MSG(expr->has_execute_failed(), 0, "Cannot evaluate expression.");
|
||||
|
||||
// Last expression. Variant result will either map to a bool or an integer, in both cases returning it will give the correct plural index.
|
||||
if (i + 1 == equi_tests.size()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (bool(result)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_V_MSG(0, "Unexpected. Function should have returned. Please report this bug.");
|
||||
}
|
||||
|
||||
void TranslationPO::_cache_plural_tests(const String &p_plural_rule) {
|
||||
// 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, esp careful with this one.
|
||||
// "n != 1" (English)
|
||||
int first_ques_mark = p_plural_rule.find("?");
|
||||
if (first_ques_mark == -1) {
|
||||
equi_tests.push_back(p_plural_rule.strip_edges());
|
||||
return;
|
||||
}
|
||||
|
||||
String equi_test = p_plural_rule.substr(0, first_ques_mark).strip_edges();
|
||||
equi_tests.push_back(equi_test);
|
||||
|
||||
String after_colon = p_plural_rule.substr(p_plural_rule.find(":") + 1, p_plural_rule.length());
|
||||
_cache_plural_tests(after_colon);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// Setup the cache to make evaluating plural rule faster later on.
|
||||
plural_rule = plural_rule.replacen("(", "");
|
||||
plural_rule = plural_rule.replacen(")", "");
|
||||
_cache_plural_tests(plural_rule);
|
||||
expr.instance();
|
||||
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].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].empty(), StringName(), "Source text \"" + String(p_src_text) + "\" is registered but doesn't have a translation. Please report this bug.");
|
||||
|
||||
if (translation_map[p_context][p_src_text].size() == 1) {
|
||||
WARN_PRINT("Source string \"" + String(p_src_text) + "\" doesn't have plural translations. Use singular translation API for such as tr(), TTR() to translate \"" + String(p_src_text) + "\"");
|
||||
return translation_map[p_context][p_src_text][0];
|
||||
}
|
||||
|
||||
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 {
|
||||
// PHashTranslation uses this function to get the list of msgid.
|
||||
// Return all the keys of translation_map under "" context.
|
||||
|
||||
List<StringName> context_l;
|
||||
translation_map.get_key_list(&context_l);
|
||||
|
||||
for (auto E = context_l.front(); E; E = E->next()) {
|
||||
if (String(E->get()) != "") {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<StringName> msgid_l;
|
||||
translation_map[E->get()].get_key_list(&msgid_l);
|
||||
|
||||
for (auto E2 = msgid_l.front(); E2; E2 = E2->next()) {
|
||||
r_messages->push_back(E2->get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int TranslationPO::get_message_count() const {
|
||||
List<StringName> context_l;
|
||||
translation_map.get_key_list(&context_l);
|
||||
|
||||
int count = 0;
|
||||
for (auto E = context_l.front(); E; E = E->next()) {
|
||||
count += translation_map[E->get()].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);
|
||||
}
|
||||
92
core/string/translation_po.h
Normal file
92
core/string/translation_po.h
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/*************************************************************************/
|
||||
/* translation_po.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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
|
||||
Vector<String> equi_tests;
|
||||
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);
|
||||
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:
|
||||
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
core/string/ucaps.h
Normal file
1415
core/string/ucaps.h
Normal file
File diff suppressed because it is too large
Load diff
4899
core/string/ustring.cpp
Normal file
4899
core/string/ustring.cpp
Normal file
File diff suppressed because it is too large
Load diff
539
core/string/ustring.h
Normal file
539
core/string/ustring.h
Normal file
|
|
@ -0,0 +1,539 @@
|
|||
/*************************************************************************/
|
||||
/* ustring.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* 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_H
|
||||
#define USTRING_H
|
||||
|
||||
#include "core/templates/cowdata.h"
|
||||
#include "core/templates/vector.h"
|
||||
#include "core/typedefs.h"
|
||||
#include "core/variant/array.h"
|
||||
|
||||
/*************************************************************************/
|
||||
/* CharProxy */
|
||||
/*************************************************************************/
|
||||
|
||||
template <class 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> &cowdata) :
|
||||
_index(p_index),
|
||||
_cowdata(cowdata) {}
|
||||
|
||||
public:
|
||||
_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 &other) const {
|
||||
_cowdata.set(_index, other);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void operator=(const CharProxy<T> &other) const {
|
||||
_cowdata.set(_index, 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_ Char16String &operator=(const Char16String &p_str) {
|
||||
_cowdata._ref(p_str._cowdata);
|
||||
return *this;
|
||||
}
|
||||
_FORCE_INLINE_ Char16String(const char16_t *p_cstr) { copy_from(p_cstr); }
|
||||
|
||||
Char16String &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_ CharString &operator=(const CharString &p_str) {
|
||||
_cowdata._ref(p_str._cowdata);
|
||||
return *this;
|
||||
}
|
||||
_FORCE_INLINE_ CharString(const char *p_cstr) { copy_from(p_cstr); }
|
||||
|
||||
CharString &operator=(const char *p_cstr);
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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(int p_index) { _cowdata.remove(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+=(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 naturalnocasecmp_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;
|
||||
|
||||
/* 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 rfind(const String &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 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 is_enclosed_in(const String &p_string) const;
|
||||
bool is_subsequence_of(const String &p_string) const;
|
||||
bool is_subsequence_ofi(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, String placeholder = "{_}") const;
|
||||
String replace_first(const String &p_key, const String &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 repeat(int p_count) const;
|
||||
String insert(int p_at_pos, const String &p_string) 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_suffix(const String &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(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);
|
||||
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);
|
||||
bool is_numeric() const;
|
||||
|
||||
double to_float() const;
|
||||
int64_t hex_to_int(bool p_with_prefix = true) const;
|
||||
int64_t bin_to_int(bool p_with_prefix = true) 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);
|
||||
|
||||
String capitalize() const;
|
||||
String camelcase_to_underscore(bool lowercase = true) const;
|
||||
|
||||
String get_with_code_lines() const;
|
||||
int get_slice_count(String p_splitter) const;
|
||||
String get_slice(String 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> rsplit(const String &p_splitter, bool p_allow_empty = true, int p_maxsplit = 0) const;
|
||||
Vector<String> split_spaces() const;
|
||||
Vector<float> 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(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 countn(const String &p_string, int p_from = 0, int p_to = 0) const;
|
||||
|
||||
String left(int p_pos) const;
|
||||
String right(int p_pos) 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 plus_file(const String &p_file) const;
|
||||
char32_t ord_at(int p_idx) const;
|
||||
|
||||
void erase(int p_pos, int p_chars);
|
||||
|
||||
CharString ascii(bool p_allow_extended = false) const;
|
||||
CharString utf8() const;
|
||||
bool parse_utf8(const char *p_utf8, int p_len = -1); //return true on error
|
||||
static String utf8(const char *p_utf8, int p_len = -1);
|
||||
|
||||
Char16String utf16() const;
|
||||
bool parse_utf16(const char16_t *p_utf16, int p_len = -1); //return true on error
|
||||
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 empty() const { return length() == 0; }
|
||||
|
||||
// path functions
|
||||
bool is_abs_path() const;
|
||||
bool is_rel_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;
|
||||
|
||||
String xml_escape(bool p_escape_quotes = false) const;
|
||||
String xml_unescape() const;
|
||||
String http_escape() const;
|
||||
String http_unescape() const;
|
||||
String c_escape() const;
|
||||
String c_escape_multiline() const;
|
||||
String c_unescape() const;
|
||||
String json_escape() const;
|
||||
String word_wrap(int p_chars_per_line) const;
|
||||
|
||||
String percent_encode() const;
|
||||
String percent_decode() const;
|
||||
|
||||
String property_name_encode() const;
|
||||
|
||||
bool is_valid_identifier() const;
|
||||
bool is_valid_integer() 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
|
||||
*/
|
||||
/* String(char32_t p_char);*/
|
||||
|
||||
_FORCE_INLINE_ String() {}
|
||||
_FORCE_INLINE_ String(const String &p_str) { _cowdata._ref(p_str._cowdata); }
|
||||
String &operator=(const String &p_str) {
|
||||
_cowdata._ref(p_str._cowdata);
|
||||
return *this;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
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 TTR(m_value) (String())
|
||||
#define TTRN(m_value) (String())
|
||||
#define DTR(m_value) (String())
|
||||
#define DTRN(m_value) (String())
|
||||
#define TTRC(m_value) (m_value)
|
||||
#define TTRGET(m_value) (m_value)
|
||||
#endif
|
||||
|
||||
// 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 = "");
|
||||
|
||||
bool is_symbol(char32_t c);
|
||||
bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end);
|
||||
|
||||
#endif // USTRING_H
|
||||
Loading…
Add table
Add a link
Reference in a new issue