Skip to content

Commit f6fc2f4

Browse files
committed
Core: Remove skip_cr argument from String
1 parent 9283328 commit f6fc2f4

File tree

11 files changed

+101
-61
lines changed

11 files changed

+101
-61
lines changed

core/io/file_access.compat.inc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ void FileAccess::store_pascal_string_bind_compat_78289(const String &p_string) {
9090
store_pascal_string(p_string);
9191
}
9292

93+
String FileAccess::get_as_text_bind_compat_110867(bool p_skip_cr) const {
94+
String text = get_as_text();
95+
if (unlikely(p_skip_cr)) {
96+
text = text.remove_char('\r');
97+
}
98+
return text;
99+
}
100+
93101
void FileAccess::_bind_compatibility_methods() {
94102
ClassDB::bind_compatibility_static_method("FileAccess", D_METHOD("open_encrypted", "path", "mode_flags", "key"), &FileAccess::_open_encrypted_bind_compat_98918);
95103

@@ -107,6 +115,7 @@ void FileAccess::_bind_compatibility_methods() {
107115
ClassDB::bind_compatibility_method(D_METHOD("store_string", "string"), &FileAccess::store_string_bind_compat_78289);
108116
ClassDB::bind_compatibility_method(D_METHOD("store_var", "value", "full_objects"), &FileAccess::store_var_bind_compat_78289, DEFVAL(false));
109117
ClassDB::bind_compatibility_method(D_METHOD("store_pascal_string", "string"), &FileAccess::store_pascal_string_bind_compat_78289);
118+
ClassDB::bind_compatibility_method(D_METHOD("get_as_text", "skip_cr"), &FileAccess::get_as_text_bind_compat_110867, DEFVAL(false));
110119
}
111120

112121
#endif

core/io/file_access.cpp

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -467,10 +467,22 @@ String FileAccess::get_line() const {
467467
uint8_t c = get_8();
468468

469469
while (!eof_reached()) {
470-
if (c == '\n' || c == '\0' || get_error() != OK) {
470+
if (c == '\r' || c == '\n' || c == '\0' || get_error() != OK) {
471+
if (c == '\r') {
472+
// Check for Windows-style EOL.
473+
const uint64_t prev_pos = get_position() - 1;
474+
if (unlikely(get_8() != '\n')) {
475+
// HACK: We can't simply check the next value in a vacuum, so we risk triggering
476+
// an EOL false-positive in the unlikely event that this `\r` was the final
477+
// value of the file. Unilaterally work around by re-reading the *previous*
478+
// byte (the starting `\r`) to ensure `get_error()` returns `OK`.
479+
const_cast<FileAccess *>(this)->seek(prev_pos);
480+
get_8();
481+
}
482+
}
471483
line.push_back(0);
472484
return String::utf8(line.get_data());
473-
} else if (c != '\r') {
485+
} else {
474486
line.push_back(char(c));
475487
}
476488

@@ -538,11 +550,11 @@ Vector<String> FileAccess::get_csv_line(const String &p_delim) const {
538550
return strings;
539551
}
540552

541-
String FileAccess::get_as_text(bool p_skip_cr) const {
553+
String FileAccess::get_as_text() const {
542554
uint64_t original_pos = get_position();
543555
const_cast<FileAccess *>(this)->seek(0);
544556

545-
String text = get_as_utf8_string(p_skip_cr);
557+
String text = get_as_utf8_string();
546558

547559
const_cast<FileAccess *>(this)->seek(original_pos);
548560

@@ -570,7 +582,7 @@ Vector<uint8_t> FileAccess::get_buffer(int64_t p_length) const {
570582
return data;
571583
}
572584

573-
String FileAccess::get_as_utf8_string(bool p_skip_cr) const {
585+
String FileAccess::get_as_utf8_string() const {
574586
Vector<uint8_t> sourcef;
575587
uint64_t len = get_length();
576588
sourcef.resize(len + 1);
@@ -581,7 +593,7 @@ String FileAccess::get_as_utf8_string(bool p_skip_cr) const {
581593
w[len] = 0;
582594

583595
String s;
584-
s.append_utf8((const char *)w, len, p_skip_cr);
596+
s.append_utf8((const char *)w, len);
585597
return s;
586598
}
587599

@@ -987,7 +999,7 @@ void FileAccess::_bind_methods() {
987999
ClassDB::bind_method(D_METHOD("get_buffer", "length"), (Vector<uint8_t> (FileAccess::*)(int64_t) const) & FileAccess::get_buffer);
9881000
ClassDB::bind_method(D_METHOD("get_line"), &FileAccess::get_line);
9891001
ClassDB::bind_method(D_METHOD("get_csv_line", "delim"), &FileAccess::get_csv_line, DEFVAL(","));
990-
ClassDB::bind_method(D_METHOD("get_as_text", "skip_cr"), &FileAccess::get_as_text, DEFVAL(false));
1002+
ClassDB::bind_method(D_METHOD("get_as_text"), &FileAccess::get_as_text);
9911003
ClassDB::bind_static_method("FileAccess", D_METHOD("get_md5", "path"), &FileAccess::get_md5);
9921004
ClassDB::bind_static_method("FileAccess", D_METHOD("get_sha256", "path"), &FileAccess::get_sha256);
9931005
ClassDB::bind_method(D_METHOD("is_big_endian"), &FileAccess::is_big_endian);

core/io/file_access.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ class FileAccess : public RefCounted {
132132
void store_line_bind_compat_78289(const String &p_line);
133133
void store_csv_line_bind_compat_78289(const Vector<String> &p_values, const String &p_delim = ",");
134134
void store_pascal_string_bind_compat_78289(const String &p_string);
135+
String get_as_text_bind_compat_110867(bool p_skip_cr) const;
135136

136137
static void _bind_compatibility_methods();
137138
#endif
@@ -188,8 +189,8 @@ class FileAccess : public RefCounted {
188189
virtual String get_line() const;
189190
virtual String get_token() const;
190191
virtual Vector<String> get_csv_line(const String &p_delim = ",") const;
191-
String get_as_text(bool p_skip_cr = false) const;
192-
virtual String get_as_utf8_string(bool p_skip_cr = false) const;
192+
String get_as_text() const;
193+
virtual String get_as_utf8_string() const;
193194

194195
/**
195196

core/string/ustring.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1763,7 +1763,7 @@ Error String::append_ascii(const Span<char> &p_range) {
17631763
return decode_failed ? ERR_INVALID_DATA : OK;
17641764
}
17651765

1766-
Error String::append_utf8(const char *p_utf8, int p_len, bool p_skip_cr) {
1766+
Error String::append_utf8(const char *p_utf8, int p_len) {
17671767
if (!p_utf8) {
17681768
return ERR_INVALID_DATA;
17691769
}
@@ -1796,11 +1796,6 @@ Error String::append_utf8(const char *p_utf8, int p_len, bool p_skip_cr) {
17961796

17971797
while (ptrtmp < ptr_limit && *ptrtmp) {
17981798
uint8_t c = *ptrtmp;
1799-
1800-
if (p_skip_cr && c == '\r') {
1801-
++ptrtmp;
1802-
continue;
1803-
}
18041799
uint32_t unicode = _replacement_char;
18051800
uint32_t size = 1;
18061801

core/string/ustring.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -525,9 +525,9 @@ class [[nodiscard]] String {
525525
}
526526

527527
CharString utf8(Vector<uint8_t> *r_ch_length_map = nullptr) const;
528-
Error append_utf8(const char *p_utf8, int p_len = -1, bool p_skip_cr = false);
529-
Error append_utf8(const Span<char> &p_range, bool p_skip_cr = false) {
530-
return append_utf8(p_range.ptr(), p_range.size(), p_skip_cr);
528+
Error append_utf8(const char *p_utf8, int p_len = -1);
529+
Error append_utf8(const Span<char> &p_range) {
530+
return append_utf8(p_range.ptr(), p_range.size());
531531
}
532532
static String utf8(const char *p_utf8, int p_len = -1) {
533533
String ret;

doc/classes/FileAccess.xml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,8 @@
133133
</method>
134134
<method name="get_as_text" qualifiers="const">
135135
<return type="String" />
136-
<param index="0" name="skip_cr" type="bool" default="false" />
137136
<description>
138137
Returns the whole file as a [String]. Text is interpreted as being UTF-8 encoded. This ignores the file cursor and does not affect it.
139-
If [param skip_cr] is [code]true[/code], carriage return characters ([code]\r[/code], CR) will be ignored when parsing the UTF-8, so that only line feed characters ([code]\n[/code], LF) represent a new line (Unix convention).
140138
</description>
141139
</method>
142140
<method name="get_buffer" qualifiers="const">

misc/extension_api_validation/4.5-stable.expected

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,10 @@ Validate extension JSON: JSON file: Field was added in a way that breaks compati
1414
Validate extension JSON: JSON file: Field was added in a way that breaks compatibility 'classes/Control/methods/has_focus': arguments
1515

1616
Optional argument added. Compatibility methods registered.
17+
18+
19+
GH-110867
20+
---------
21+
ERROR: Validate extension JSON: Missing field in current API 'classes/FileAccess/methods/get_as_text': arguments. This is a bug.
22+
23+
Optional argument removed. Compatibility method registered.

platform/android/file_access_filesystem_jandroid.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,18 +200,19 @@ String FileAccessFilesystemJAndroid::get_line() const {
200200

201201
for (; bytes_read > 0; line_buffer_position++, bytes_read--) {
202202
uint8_t elem = line_buffer[line_buffer_position];
203-
if (elem == '\n' || elem == '\0') {
203+
if (elem == '\r' || elem == '\n' || elem == '\0') {
204204
// Found the end of the line
205-
const_cast<FileAccessFilesystemJAndroid *>(this)->seek(start_position + line_buffer_position + 1);
206-
if (result.append_utf8((const char *)line_buffer.ptr(), line_buffer_position, true)) {
205+
const bool is_crlf = elem == '\r' && line_buffer_position + 1 < current_buffer_size && line_buffer[line_buffer_position + 1] == '\n';
206+
const_cast<FileAccessFilesystemJAndroid *>(this)->seek(start_position + line_buffer_position + (is_crlf ? 2 : 1));
207+
if (result.append_utf8((const char *)line_buffer.ptr(), line_buffer_position)) {
207208
return String();
208209
}
209210
return result;
210211
}
211212
}
212213
}
213214

214-
if (result.append_utf8((const char *)line_buffer.ptr(), line_buffer_position, true)) {
215+
if (result.append_utf8((const char *)line_buffer.ptr(), line_buffer_position)) {
215216
return String();
216217
}
217218
return result;

tests/core/io/test_file_access.h

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -82,29 +82,57 @@ TEST_CASE("[FileAccess] CSV read") {
8282
}
8383

8484
TEST_CASE("[FileAccess] Get as UTF-8 String") {
85-
Ref<FileAccess> f_lf = FileAccess::open(TestUtils::get_data_path("line_endings_lf.test.txt"), FileAccess::READ);
86-
REQUIRE(f_lf.is_valid());
87-
String s_lf = f_lf->get_as_utf8_string();
88-
f_lf->seek(0);
89-
String s_lf_nocr = f_lf->get_as_utf8_string(true);
90-
CHECK(s_lf == "Hello darkness\nMy old friend\nI've come to talk\nWith you again\n");
91-
CHECK(s_lf_nocr == "Hello darkness\nMy old friend\nI've come to talk\nWith you again\n");
92-
93-
Ref<FileAccess> f_crlf = FileAccess::open(TestUtils::get_data_path("line_endings_crlf.test.txt"), FileAccess::READ);
94-
REQUIRE(f_crlf.is_valid());
95-
String s_crlf = f_crlf->get_as_utf8_string();
96-
f_crlf->seek(0);
97-
String s_crlf_nocr = f_crlf->get_as_utf8_string(true);
98-
CHECK(s_crlf == "Hello darkness\r\nMy old friend\r\nI've come to talk\r\nWith you again\r\n");
99-
CHECK(s_crlf_nocr == "Hello darkness\nMy old friend\nI've come to talk\nWith you again\n");
100-
101-
Ref<FileAccess> f_cr = FileAccess::open(TestUtils::get_data_path("line_endings_cr.test.txt"), FileAccess::READ);
102-
REQUIRE(f_cr.is_valid());
103-
String s_cr = f_cr->get_as_utf8_string();
104-
f_cr->seek(0);
105-
String s_cr_nocr = f_cr->get_as_utf8_string(true);
106-
CHECK(s_cr == "Hello darkness\rMy old friend\rI've come to talk\rWith you again\r");
107-
CHECK(s_cr_nocr == "Hello darknessMy old friendI've come to talkWith you again");
85+
SUBCASE("Newline == \\n (Unix)") {
86+
Ref<FileAccess> f_lf = FileAccess::open(TestUtils::get_data_path("line_endings_lf.test.txt"), FileAccess::READ);
87+
REQUIRE(f_lf.is_valid());
88+
String s_lf = f_lf->get_as_utf8_string();
89+
CHECK(s_lf == "Hello darkness\nMy old friend\nI've come to talk\nWith you again\n");
90+
f_lf->seek(0);
91+
CHECK(f_lf->get_line() == "Hello darkness");
92+
CHECK(f_lf->get_line() == "My old friend");
93+
CHECK(f_lf->get_line() == "I've come to talk");
94+
CHECK(f_lf->get_line() == "With you again");
95+
CHECK(f_lf->get_error() == Error::OK);
96+
}
97+
98+
SUBCASE("Newline == \\r\\n (Windows)") {
99+
Ref<FileAccess> f_crlf = FileAccess::open(TestUtils::get_data_path("line_endings_crlf.test.txt"), FileAccess::READ);
100+
REQUIRE(f_crlf.is_valid());
101+
String s_crlf = f_crlf->get_as_utf8_string();
102+
CHECK(s_crlf == "Hello darkness\r\nMy old friend\r\nI've come to talk\r\nWith you again\r\n");
103+
f_crlf->seek(0);
104+
CHECK(f_crlf->get_line() == "Hello darkness");
105+
CHECK(f_crlf->get_line() == "My old friend");
106+
CHECK(f_crlf->get_line() == "I've come to talk");
107+
CHECK(f_crlf->get_line() == "With you again");
108+
CHECK(f_crlf->get_error() == Error::OK);
109+
}
110+
111+
SUBCASE("Newline == \\r (Legacy macOS)") {
112+
Ref<FileAccess> f_cr = FileAccess::open(TestUtils::get_data_path("line_endings_cr.test.txt"), FileAccess::READ);
113+
REQUIRE(f_cr.is_valid());
114+
String s_cr = f_cr->get_as_utf8_string();
115+
CHECK(s_cr == "Hello darkness\rMy old friend\rI've come to talk\rWith you again\r");
116+
f_cr->seek(0);
117+
CHECK(f_cr->get_line() == "Hello darkness");
118+
CHECK(f_cr->get_line() == "My old friend");
119+
CHECK(f_cr->get_line() == "I've come to talk");
120+
CHECK(f_cr->get_line() == "With you again");
121+
CHECK(f_cr->get_error() == Error::OK);
122+
}
123+
124+
SUBCASE("Newline == Mixed") {
125+
Ref<FileAccess> f_mix = FileAccess::open(TestUtils::get_data_path("line_endings_mixed.test.txt"), FileAccess::READ);
126+
REQUIRE(f_mix.is_valid());
127+
String s_mix = f_mix->get_as_utf8_string();
128+
CHECK(s_mix == "Hello darkness\nMy old friend\r\nI've come to talk\rWith you again");
129+
f_mix->seek(0);
130+
CHECK(f_mix->get_line() == "Hello darkness");
131+
CHECK(f_mix->get_line() == "My old friend");
132+
CHECK(f_mix->get_line() == "I've come to talk");
133+
CHECK(f_mix->get_line() == "With you again");
134+
CHECK(f_mix->get_error() == Error::ERR_FILE_EOF); // Not a bug; the file lacks a final newline.
135+
}
108136
}
109137

110138
TEST_CASE("[FileAccess] Get/Store floating point values") {

tests/core/string/test_string.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -156,20 +156,6 @@ TEST_CASE("[String] UTF16 with BOM") {
156156
CHECK(String::utf16(cs) == s);
157157
}
158158

159-
TEST_CASE("[String] UTF8 with CR") {
160-
const String base = U"Hello darkness\r\nMy old friend\nI've come to talk\rWith you again";
161-
162-
String keep_cr;
163-
Error err = keep_cr.append_utf8(base.utf8().get_data());
164-
CHECK(err == OK);
165-
CHECK(keep_cr == base);
166-
167-
String no_cr;
168-
err = no_cr.append_utf8(base.utf8().get_data(), -1, true); // Skip CR.
169-
CHECK(err == OK);
170-
CHECK(no_cr == base.replace("\r", ""));
171-
}
172-
173159
TEST_CASE("[String] Invalid UTF8 (non shortest form sequence)") {
174160
ERR_PRINT_OFF
175161
// Examples from the unicode standard : 3.9 Unicode Encoding Forms - Table 3.8.

0 commit comments

Comments
 (0)