diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/dvector.h | 2 | ||||
-rw-r--r-- | core/io/http_client.cpp | 36 | ||||
-rw-r--r-- | core/io/http_client.h | 1 | ||||
-rw-r--r-- | core/io/stream_peer_ssl.cpp | 17 | ||||
-rw-r--r-- | core/io/stream_peer_ssl.h | 8 | ||||
-rw-r--r-- | core/message_queue.cpp | 2 | ||||
-rw-r--r-- | core/object.cpp | 6 | ||||
-rw-r--r-- | core/os/os.cpp | 17 | ||||
-rw-r--r-- | core/os/os.h | 10 | ||||
-rw-r--r-- | core/project_settings.cpp | 22 | ||||
-rw-r--r-- | core/project_settings.h | 13 | ||||
-rw-r--r-- | core/register_core_types.cpp | 2 | ||||
-rw-r--r-- | core/script_language.cpp | 77 | ||||
-rw-r--r-- | core/script_language.h | 23 | ||||
-rw-r--r-- | core/typedefs.h | 7 | ||||
-rw-r--r-- | core/ustring.cpp | 40 |
16 files changed, 238 insertions, 45 deletions
diff --git a/core/dvector.h b/core/dvector.h index c0190fb9e3..e03a755e6c 100644 --- a/core/dvector.h +++ b/core/dvector.h @@ -150,7 +150,7 @@ class PoolVector { } if (old_alloc->refcount.unref() == true) { - //this should never happen but.. + //this should never happen but.. #ifdef DEBUG_ENABLED MemoryPool::alloc_mutex->lock(); diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index fcbb22b5de..f1620f1493 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -279,6 +279,7 @@ void HTTPClient::close() { chunk_left = 0; read_until_eof = false; response_num = 0; + handshaking = false; } Error HTTPClient::poll() { @@ -327,16 +328,40 @@ Error HTTPClient::poll() { } break; case StreamPeerTCP::STATUS_CONNECTED: { if (ssl) { - Ref<StreamPeerSSL> ssl = StreamPeerSSL::create(); - Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, conn_host); - if (err != OK) { + Ref<StreamPeerSSL> ssl; + if (!handshaking) { + // Connect the StreamPeerSSL and start handshaking + ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create()); + ssl->set_blocking_handshake_enabled(false); + Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, conn_host); + if (err != OK) { + close(); + status = STATUS_SSL_HANDSHAKE_ERROR; + return ERR_CANT_CONNECT; + } + connection = ssl; + handshaking = true; + } else { + // We are already handshaking, which means we can use your already active SSL connection + ssl = static_cast<Ref<StreamPeerSSL> >(connection); + ssl->poll(); // Try to finish the handshake + } + + if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) { + // Handshake has been successfull + handshaking = false; + status = STATUS_CONNECTED; + return OK; + } else if (ssl->get_status() != StreamPeerSSL::STATUS_HANDSHAKING) { + // Handshake has failed close(); status = STATUS_SSL_HANDSHAKE_ERROR; return ERR_CANT_CONNECT; } - connection = ssl; + // ... we will need to poll more for handshake to finish + } else { + status = STATUS_CONNECTED; } - status = STATUS_CONNECTED; return OK; } break; case StreamPeerTCP::STATUS_ERROR: @@ -669,6 +694,7 @@ HTTPClient::HTTPClient() { response_num = 0; ssl = false; blocking = false; + handshaking = false; read_chunk_size = 4096; } diff --git a/core/io/http_client.h b/core/io/http_client.h index 38ec82ce8c..82b56b01db 100644 --- a/core/io/http_client.h +++ b/core/io/http_client.h @@ -165,6 +165,7 @@ private: bool ssl; bool ssl_verify_host; bool blocking; + bool handshaking; Vector<uint8_t> response_str; diff --git a/core/io/stream_peer_ssl.cpp b/core/io/stream_peer_ssl.cpp index 012ba78c6d..c71af6b641 100644 --- a/core/io/stream_peer_ssl.cpp +++ b/core/io/stream_peer_ssl.cpp @@ -52,6 +52,14 @@ bool StreamPeerSSL::is_available() { return available; } +void StreamPeerSSL::set_blocking_handshake_enabled(bool p_enabled) { + blocking_handshake = p_enabled; +} + +bool StreamPeerSSL::is_blocking_handshake_enabled() const { + return blocking_handshake; +} + PoolByteArray StreamPeerSSL::get_project_cert_array() { PoolByteArray out; @@ -84,16 +92,21 @@ PoolByteArray StreamPeerSSL::get_project_cert_array() { void StreamPeerSSL::_bind_methods() { ClassDB::bind_method(D_METHOD("poll"), &StreamPeerSSL::poll); - ClassDB::bind_method(D_METHOD("accept_stream", "stream"), &StreamPeerSSL::accept_stream); + ClassDB::bind_method(D_METHOD("accept_stream"), &StreamPeerSSL::accept_stream); ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname"), &StreamPeerSSL::connect_to_stream, DEFVAL(false), DEFVAL(String())); ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerSSL::get_status); ClassDB::bind_method(D_METHOD("disconnect_from_stream"), &StreamPeerSSL::disconnect_from_stream); + ClassDB::bind_method(D_METHOD("set_blocking_handshake_enabled", "enabled"), &StreamPeerSSL::set_blocking_handshake_enabled); + ClassDB::bind_method(D_METHOD("is_blocking_handshake_enabled"), &StreamPeerSSL::is_blocking_handshake_enabled); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "blocking_handshake"), "set_blocking_handshake_enabled", "is_blocking_handshake_enabled"); BIND_ENUM_CONSTANT(STATUS_DISCONNECTED); BIND_ENUM_CONSTANT(STATUS_CONNECTED); - BIND_ENUM_CONSTANT(STATUS_ERROR_NO_CERTIFICATE); + BIND_ENUM_CONSTANT(STATUS_ERROR); BIND_ENUM_CONSTANT(STATUS_ERROR_HOSTNAME_MISMATCH); } StreamPeerSSL::StreamPeerSSL() { + blocking_handshake = true; } diff --git a/core/io/stream_peer_ssl.h b/core/io/stream_peer_ssl.h index 77301a7c87..870704e875 100644 --- a/core/io/stream_peer_ssl.h +++ b/core/io/stream_peer_ssl.h @@ -49,14 +49,20 @@ protected: friend class Main; static bool initialize_certs; + bool blocking_handshake; + public: enum Status { STATUS_DISCONNECTED, + STATUS_HANDSHAKING, STATUS_CONNECTED, - STATUS_ERROR_NO_CERTIFICATE, + STATUS_ERROR, STATUS_ERROR_HOSTNAME_MISMATCH }; + void set_blocking_handshake_enabled(bool p_enabled); + bool is_blocking_handshake_enabled() const; + virtual void poll() = 0; virtual Error accept_stream(Ref<StreamPeer> p_base) = 0; virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String()) = 0; diff --git a/core/message_queue.cpp b/core/message_queue.cpp index 25ee6eafae..3adaad868a 100644 --- a/core/message_queue.cpp +++ b/core/message_queue.cpp @@ -342,7 +342,7 @@ MessageQueue::MessageQueue() { buffer_end = 0; buffer_max_used = 0; - buffer_size = GLOBAL_DEF("memory/limits/message_queue/max_size_kb", DEFAULT_QUEUE_SIZE_KB); + buffer_size = GLOBAL_DEF_RST("memory/limits/message_queue/max_size_kb", DEFAULT_QUEUE_SIZE_KB); buffer_size *= 1024; buffer = memnew_arr(uint8_t, buffer_size); } diff --git a/core/object.cpp b/core/object.cpp index 1d2aeb7ba5..d86c60a3b8 100644 --- a/core/object.cpp +++ b/core/object.cpp @@ -601,8 +601,12 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons _get_property_listv(p_list, p_reversed); - if (!is_class("Script")) // can still be set, but this is for userfriendlyness + if (!is_class("Script")) { // can still be set, but this is for userfriendlyness +#ifdef TOOLS_ENABLED + p_list->push_back(PropertyInfo(Variant::NIL, "Script", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); +#endif p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NONZERO)); + } #ifdef TOOLS_ENABLED if (editor_section_folding.size()) { p_list->push_back(PropertyInfo(Variant::ARRAY, CoreStringNames::get_singleton()->_sections_unfolded, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); diff --git a/core/os/os.cpp b/core/os/os.cpp index 5eed10e30c..89866e4044 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -614,6 +614,9 @@ bool OS::has_feature(const String &p_feature) { if (_check_internal_feature_support(p_feature)) return true; + if (ProjectSettings::get_singleton()->has_custom_feature(p_feature)) + return true; + return false; } @@ -656,9 +659,23 @@ const char *OS::get_audio_driver_name(int p_driver) const { return AudioDriverManager::get_driver(p_driver)->get_name(); } +void OS::set_restart_on_exit(bool p_restart, const List<String> &p_restart_arguments) { + restart_on_exit = p_restart; + restart_commandline = p_restart_arguments; +} + +bool OS::is_restart_on_exit_set() const { + return restart_on_exit; +} + +List<String> OS::get_restart_on_exit_arguments() const { + return restart_commandline; +} + OS::OS() { void *volatile stack_bottom; + restart_on_exit = false; last_error = NULL; singleton = this; _keep_screen_on = true; // set default value to true, because this had been true before godot 2.0. diff --git a/core/os/os.h b/core/os/os.h index adf01a90e7..4e0cb003fb 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -74,6 +74,9 @@ class OS { CompositeLogger *_logger; + bool restart_on_exit; + List<String> restart_commandline; + protected: void _set_logger(CompositeLogger *p_logger); @@ -182,7 +185,7 @@ public: virtual int get_video_driver_count() const; virtual const char *get_video_driver_name(int p_driver) const; - + virtual int get_current_video_driver() const = 0; virtual int get_audio_driver_count() const; virtual const char *get_audio_driver_name(int p_driver) const; @@ -496,6 +499,11 @@ public: bool is_layered_allowed() const { return _allow_layered; } bool is_hidpi_allowed() const { return _allow_hidpi; } + + void set_restart_on_exit(bool p_restart, const List<String> &p_restart_arguments); + bool is_restart_on_exit_set() const; + List<String> get_restart_on_exit_arguments() const; + OS(); virtual ~OS(); }; diff --git a/core/project_settings.cpp b/core/project_settings.cpp index 7f9f4b638a..60e8933751 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -105,6 +105,11 @@ void ProjectSettings::set_initial_value(const String &p_name, const Variant &p_v ERR_FAIL_COND(!props.has(p_name)); props[p_name].initial = p_value; } +void ProjectSettings::set_restart_if_changed(const String &p_name, bool p_restart) { + + ERR_FAIL_COND(!props.has(p_name)); + props[p_name].restart_if_changed = p_restart; +} String ProjectSettings::globalize_path(const String &p_path) const { @@ -225,6 +230,9 @@ void ProjectSettings::_get_property_list(List<PropertyInfo> *p_list) const { else vc.flags = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE; + if (v->restart_if_changed) { + vc.flags |= PROPERTY_USAGE_RESTART_IF_CHANGED; + } vclist.insert(vc); } @@ -515,7 +523,11 @@ Error ProjectSettings::_load_settings_text(const String p_path) { } } else { // config_version is checked and dropped - set(section + "/" + assign, value); + if (section == String()) { + set(assign, value); + } else { + set(section + "/" + assign, value); + } } } else if (next_tag.name != String()) { section = next_tag.name; @@ -813,7 +825,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust return OK; } -Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default) { +Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed) { Variant ret; if (!ProjectSettings::get_singleton()->has_setting(p_var)) { @@ -823,6 +835,7 @@ Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default) { ProjectSettings::get_singleton()->set_initial_value(p_var, p_default); ProjectSettings::get_singleton()->set_builtin_order(p_var); + ProjectSettings::get_singleton()->set_restart_if_changed(p_var, p_restart_if_changed); return ret; } @@ -908,6 +921,10 @@ Variant ProjectSettings::get_setting(const String &p_setting) const { return get(p_setting); } +bool ProjectSettings::has_custom_feature(const String &p_feature) const { + return custom_features.has(p_feature); +} + void ProjectSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("has_setting", "name"), &ProjectSettings::has_setting); @@ -1072,7 +1089,6 @@ ProjectSettings::ProjectSettings() { custom_prop_info["rendering/threads/thread_model"] = PropertyInfo(Variant::INT, "rendering/threads/thread_model", PROPERTY_HINT_ENUM, "Single-Unsafe,Single-Safe,Multi-Threaded"); custom_prop_info["physics/2d/thread_model"] = PropertyInfo(Variant::INT, "physics/2d/thread_model", PROPERTY_HINT_ENUM, "Single-Unsafe,Single-Safe,Multi-Threaded"); custom_prop_info["rendering/quality/intended_usage/framebuffer_allocation"] = PropertyInfo(Variant::INT, "rendering/quality/intended_usage/framebuffer_allocation", PROPERTY_HINT_ENUM, "2D,2D Without Sampling,3D,3D Without Effects"); - GLOBAL_DEF("rendering/quality/intended_usage/framebuffer_mode", 2); GLOBAL_DEF("debug/settings/profiler/max_functions", 16384); diff --git a/core/project_settings.h b/core/project_settings.h index 66f3ed954e..75ebc5acc8 100644 --- a/core/project_settings.h +++ b/core/project_settings.h @@ -59,18 +59,21 @@ protected: Variant initial; bool hide_from_editor; bool overridden; + bool restart_if_changed; VariantContainer() : order(0), persist(false), hide_from_editor(false), - overridden(false) { + overridden(false), + restart_if_changed(false) { } VariantContainer(const Variant &p_variant, int p_order, bool p_persist = false) : order(p_order), persist(p_persist), variant(p_variant), hide_from_editor(false), - overridden(false) { + overridden(false), + restart_if_changed(false) { } }; @@ -120,6 +123,7 @@ public: String globalize_path(const String &p_path) const; void set_initial_value(const String &p_name, const Variant &p_value); + void set_restart_if_changed(const String &p_name, bool p_restart); bool property_can_revert(const String &p_name); Variant property_get_revert(const String &p_name); @@ -151,13 +155,16 @@ public: void set_registering_order(bool p_enable); + bool has_custom_feature(const String &p_feature) const; + ProjectSettings(); ~ProjectSettings(); }; //not a macro any longer -Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default); +Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed = false); #define GLOBAL_DEF(m_var, m_value) _GLOBAL_DEF(m_var, m_value) +#define GLOBAL_DEF_RST(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true) #define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get(m_var) #endif diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 2a611ccf6a..9bcc2d4530 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -191,7 +191,7 @@ void register_core_types() { void register_core_settings() { //since in register core types, globals may not e present - GLOBAL_DEF("network/limits/packet_peer_stream/max_buffer_po2", (16)); + GLOBAL_DEF_RST("network/limits/packet_peer_stream/max_buffer_po2", (16)); } void register_core_singletons() { diff --git a/core/script_language.cpp b/core/script_language.cpp index acbe3b34db..37ba3cfc62 100644 --- a/core/script_language.cpp +++ b/core/script_language.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "script_language.h" +#include "project_settings.h" ScriptLanguage *ScriptServer::_languages[MAX_LANGUAGES]; int ScriptServer::_language_count = 0; @@ -103,6 +104,20 @@ void ScriptServer::unregister_language(ScriptLanguage *p_language) { void ScriptServer::init_languages() { + { //load global classes + global_classes_clear(); + if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) { + Array script_classes = ProjectSettings::get_singleton()->get("_global_script_classes"); + + for (int i = 0; i < script_classes.size(); i++) { + Dictionary c = script_classes[i]; + if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) + continue; + add_global_class(c["class"], c["base"], c["language"], c["path"]); + } + } + } + for (int i = 0; i < _language_count; i++) { _languages[i]->init(); } @@ -113,6 +128,7 @@ void ScriptServer::finish_languages() { for (int i = 0; i < _language_count; i++) { _languages[i]->finish(); } + global_classes_clear(); } void ScriptServer::set_reload_scripts_on_save(bool p_enable) { @@ -139,6 +155,67 @@ void ScriptServer::thread_exit() { } } +HashMap<StringName, ScriptServer::GlobalScriptClass> ScriptServer::global_classes; + +void ScriptServer::global_classes_clear() { + global_classes.clear(); +} + +void ScriptServer::add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path) { + GlobalScriptClass g; + g.language = p_language; + g.path = p_path; + g.base = p_base; + global_classes[p_class] = g; +} +void ScriptServer::remove_global_class(const StringName &p_class) { + global_classes.erase(p_class); +} +bool ScriptServer::is_global_class(const StringName &p_class) { + return global_classes.has(p_class); +} +StringName ScriptServer::get_global_class_language(const StringName &p_class) { + ERR_FAIL_COND_V(!global_classes.has(p_class), StringName()); + return global_classes[p_class].language; +} +String ScriptServer::get_global_class_path(const String &p_class) { + ERR_FAIL_COND_V(!global_classes.has(p_class), String()); + return global_classes[p_class].path; +} + +StringName ScriptServer::get_global_class_base(const String &p_class) { + ERR_FAIL_COND_V(!global_classes.has(p_class), String()); + return global_classes[p_class].base; +} +void ScriptServer::get_global_class_list(List<StringName> *r_global_classes) { + const StringName *K = NULL; + List<StringName> classes; + while ((K = global_classes.next(K))) { + classes.push_back(*K); + } + classes.sort_custom<StringName::AlphCompare>(); + for (List<StringName>::Element *E = classes.front(); E; E = E->next()) { + r_global_classes->push_back(E->get()); + } +} +void ScriptServer::save_global_classes() { + List<StringName> gc; + get_global_class_list(&gc); + Array gcarr; + for (List<StringName>::Element *E = gc.front(); E; E = E->next()) { + Dictionary d; + d["class"] = E->get(); + d["language"] = global_classes[E->get()].language; + d["path"] = global_classes[E->get()].path; + d["base"] = global_classes[E->get()].base; + gcarr.push_back(d); + } + + ProjectSettings::get_singleton()->set("_global_script_classes", gcarr); + ProjectSettings::get_singleton()->save(); +} + +//////////////////// void ScriptInstance::get_property_state(List<Pair<StringName, Variant> > &state) { List<PropertyInfo> pinfo; diff --git a/core/script_language.h b/core/script_language.h index e7748f93e2..2950b35109 100644 --- a/core/script_language.h +++ b/core/script_language.h @@ -54,6 +54,14 @@ class ScriptServer { static bool scripting_enabled; static bool reload_scripts_on_save; + struct GlobalScriptClass { + StringName language; + String path; + String base; + }; + + static HashMap<StringName, GlobalScriptClass> global_classes; + public: static ScriptEditRequestFunction edit_request_func; @@ -70,6 +78,16 @@ public: static void thread_enter(); static void thread_exit(); + static void global_classes_clear(); + static void add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path); + static void remove_global_class(const StringName &p_class); + static bool is_global_class(const StringName &p_class); + static StringName get_global_class_language(const StringName &p_class); + static String get_global_class_path(const String &p_class); + static StringName get_global_class_base(const String &p_class); + static void get_global_class_list(List<StringName> *r_global_classes); + static void save_global_classes(); + static void init_languages(); static void finish_languages(); }; @@ -285,7 +303,10 @@ public: virtual void frame(); - virtual ~ScriptLanguage(){}; + virtual bool handles_global_class_type(const String &p_type) const { return false; } + virtual String get_global_class_name(const String &p_path, String *r_base_type = NULL) const { return String(); } + + virtual ~ScriptLanguage() {} }; extern uint8_t script_encryption_key[32]; diff --git a/core/typedefs.h b/core/typedefs.h index 4758a5408d..71771ea4e6 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -74,7 +74,7 @@ T *_nullptr() { #define OFFSET_OF(st, m) \ ((size_t)((char *)&(_nullptr<st>()->m) - (char *)0)) - /** +/** * Some platforms (devices) not define NULL */ @@ -82,7 +82,7 @@ T *_nullptr() { #define NULL 0 #endif - /** +/** * Windows defines a lot of badly stuff we'll never ever use. undefine it. */ @@ -97,6 +97,7 @@ T *_nullptr() { #undef CLAMP // override standard definition #undef Error #undef OK +#undef CONNECT_DEFERRED // override from Windows SDK, clashes with Object enum #endif #include "int_types.h" @@ -104,7 +105,7 @@ T *_nullptr() { #include "error_list.h" #include "error_macros.h" - /** Generic ABS function, for math uses please use Math::abs */ +/** Generic ABS function, for math uses please use Math::abs */ #ifndef ABS #define ABS(m_v) ((m_v < 0) ? (-(m_v)) : (m_v)) diff --git a/core/ustring.cpp b/core/ustring.cpp index 51f05468e2..bee5f5ffdb 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -757,36 +757,32 @@ Vector<String> String::rsplit(const String &p_splitter, bool p_allow_empty, int Vector<String> ret; const int len = length(); - int from = len; + int remaining_len = len; while (true) { - int end = rfind(p_splitter, from); - if (end < 0) - end = 0; - - if (p_allow_empty || (end < from)) { - const String str = substr(end > 0 ? end + p_splitter.length() : end, end > 0 ? from - end : from + 2); - - if (p_maxsplit <= 0) { - ret.push_back(str); - } else if (p_maxsplit > 0) { - - // Put rest of the string and leave cycle. - if (p_maxsplit == ret.size()) { - ret.push_back(substr(0, from + 2)); - break; - } - - // Otherwise, push items until positive limit is reached. - ret.push_back(str); + if (remaining_len < p_splitter.length() || (p_maxsplit > 0 && p_maxsplit == ret.size())) { + // no room for another splitter or hit max splits, push what's left and we're done + if (p_allow_empty || remaining_len > 0) { + ret.push_back(substr(0, remaining_len)); } + break; } - if (end == 0) + int left_edge = rfind(p_splitter, remaining_len - p_splitter.length()); + + if (left_edge < 0) { + // no more splitters, we're done + ret.push_back(substr(0, remaining_len)); break; + } + + int substr_start = left_edge + p_splitter.length(); + if (p_allow_empty || substr_start < remaining_len) { + ret.push_back(substr(substr_start, remaining_len - substr_start)); + } - from = end - p_splitter.length(); + remaining_len = left_edge; } ret.invert(); |