Repository: godotengine/godot
File: core/string/ustring.cpp (C++)

Code before:
}

bool String::has_extension(const String &p_ext) const {
	int pos = rfind_char('.');
	if (pos < 0 || pos < MAX(rfind_char('/'), rfind_char('\\')) || pos == length()) {
		return false;
	}
	const char32_t *this_str = get_data() + pos + 1;
	const char32_t *that_str = p_ext.get_data();

	while (true) {
		if (*that_str == 0 && *this_str == 0) {
			return true;
		} else if (*this_str == 0 || that_str == 0) {
			return false;
		} else if (_find_upper(*this_str) < _find_upper(*that_str)) {
			return false;
		} else if (_find_upper(*this_str) > _find_upper(*that_str)) {
			return false;
		}
		this_str++;
		that_str++;
	}

	return false;
}

bool String::has_extension(const char *p_ext) const {
	int pos = rfind_char('.');
	if (pos < 0 || pos < MAX(rfind_char('/'), rfind_char('\\')) || pos == length()) {
		return false;
	}
	const char32_t *this_str = get_data() + pos + 1;

	while (true) {
		if (*p_ext == 0 && *this_str == 0) {
			return true;
		} else if (*this_str == 0 || p_ext == 0) {
			return false;
		} else if (_find_upper(*this_str) < _find_upper(*p_ext)) {
			return false;
		} else if (_find_upper(*this_str) > _find_upper(*p_ext)) {
			return false;
		}
		this_str++;
		p_ext++;
	}

	return false;
	return false;
}

Review comment (bug):
I wouldn't do a search here, I'd just compare in reverse with the extension, and then check that there's a period ahead of that, that would process the string exactly once, and the result would only be valid if the end of the string is, ignoring case, equal to `"." + p_ext`, so I'd say:
```suggestion
	if (p_ext.length() >= length()) {
		return false;
	}
	
	const char32_t *this_str = get_data() + length() - p_ext.length();
	
	if (*this_char != '.') {
		return false;
	}
	
	this_str++;
	const char32_t *that_str = p_ext.get_data();

	while (true) {
		if (*that_str == 0 && *this_str == 0) {
			return true;
		} else if (*this_str == 0 || that_str == 0) {
			return false;
		} else if (_find_upper(*this_str) < _find_upper(*that_str)) {
			return false;
		} else if (_find_upper(*this_str) > _find_upper(*that_str)) {
			return false;
		}
		this_str++;
		that_str++;
	}

	return false;
```
Or equivalent, haven't tested and might be off on some of the size checks

This *should* check that at one position before the length of `p_ext` there is a `.`, and then traverse over the rest of the string and match the extension fully, it will fail if:
* The string being checked for an extension is shorter than or equal in length to the extension
* It doesn't have a period ahead of the extension (checked early based on just skipping)
* That the extension matches after that

Find isn't relevant as there's only exactly one place where the period can be for this check to be valid 

Code after:
}

String String::indent(const String &p_prefix) const {
	String new_string;
	int line_start = 0;

	for (int i = 0; i < length(); i++) {
		const char32_t c = operator[](i);
		if (c == '\n') {
			if (i == line_start) {
				new_string += c; // Leave empty lines empty.
			} else {
				new_string += p_prefix + substr(line_start, i - line_start + 1);
			}
			line_start = i + 1;
		}
	}
	if (line_start != length()) {
		new_string += p_prefix + substr(line_start);
	}
	return new_string;
}

String String::dedent() const {
	String new_string;
	String indent;
	bool has_indent = false;
	bool has_text = false;
	int line_start = 0;
	int indent_stop = -1;

	for (int i = 0; i < length(); i++) {
		char32_t c = operator[](i);
		if (c == '\n') {
			if (has_text) {
				new_string += substr(indent_stop, i - indent_stop);
			}
			new_string += "\n";
			has_text = false;
			line_start = i + 1;
			indent_stop = -1;
		} else if (!has_text) {
			if (c > 32) {
				has_text = true;
				if (!has_indent) {
					has_indent = true;
					indent = substr(line_start, i - line_start);
					indent_stop = i;
				}
			}
			if (has_indent && indent_stop < 0) {