Skip to content

Commit ff71542

Browse files
committed
py: Fix str.replace for case when arg 0 or 1 is empty string.
1 parent 36e75ae commit ff71542

2 files changed

Lines changed: 38 additions & 15 deletions

File tree

py/objstr.c

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,22 +1106,31 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, uint n_args, const mp_obj_t
11061106

11071107
STATIC mp_obj_t str_replace(uint n_args, const mp_obj_t *args) {
11081108
assert(MP_OBJ_IS_STR(args[0]));
1109-
assert(MP_OBJ_IS_STR(args[1]));
1110-
assert(MP_OBJ_IS_STR(args[2]));
11111109

1112-
machine_int_t max_rep = 0;
1110+
machine_int_t max_rep = -1;
11131111
if (n_args == 4) {
1114-
assert(MP_OBJ_IS_SMALL_INT(args[3]));
1115-
max_rep = MP_OBJ_SMALL_INT_VALUE(args[3]);
1112+
max_rep = mp_obj_get_int(args[3]);
11161113
if (max_rep == 0) {
11171114
return args[0];
11181115
} else if (max_rep < 0) {
1119-
max_rep = 0;
1116+
max_rep = -1;
11201117
}
11211118
}
11221119

11231120
// if max_rep is still 0 by this point we will need to do all possible replacements
11241121

1122+
// check argument types
1123+
1124+
if (!MP_OBJ_IS_STR(args[1])) {
1125+
bad_implicit_conversion(args[1]);
1126+
}
1127+
1128+
if (!MP_OBJ_IS_STR(args[2])) {
1129+
bad_implicit_conversion(args[2]);
1130+
}
1131+
1132+
// extract string data
1133+
11251134
GET_STR_DATA_LEN(args[0], str, str_len);
11261135
GET_STR_DATA_LEN(args[1], old, old_len);
11271136
GET_STR_DATA_LEN(args[2], new, new_len);
@@ -1143,8 +1152,20 @@ STATIC mp_obj_t str_replace(uint n_args, const mp_obj_t *args) {
11431152
machine_uint_t num_replacements_done = 0;
11441153
const byte *old_occurrence;
11451154
const byte *offset_ptr = str;
1146-
machine_uint_t offset_num = 0;
1147-
while ((old_occurrence = find_subbytes(offset_ptr, str_len - offset_num, old, old_len, 1)) != NULL) {
1155+
machine_uint_t str_len_remain = str_len;
1156+
if (old_len == 0) {
1157+
// if old_str is empty, copy new_str to start of replaced string
1158+
// copy the replacement string
1159+
if (data != NULL) {
1160+
memcpy(data, new, new_len);
1161+
}
1162+
replaced_str_index += new_len;
1163+
num_replacements_done++;
1164+
}
1165+
while (num_replacements_done != max_rep && str_len_remain > 0 && (old_occurrence = find_subbytes(offset_ptr, str_len_remain, old, old_len, 1)) != NULL) {
1166+
if (old_len == 0) {
1167+
old_occurrence += 1;
1168+
}
11481169
// copy from just after end of last occurrence of to-be-replaced string to right before start of next occurrence
11491170
if (data != NULL) {
11501171
memcpy(data + replaced_str_index, offset_ptr, old_occurrence - offset_ptr);
@@ -1156,19 +1177,15 @@ STATIC mp_obj_t str_replace(uint n_args, const mp_obj_t *args) {
11561177
}
11571178
replaced_str_index += new_len;
11581179
offset_ptr = old_occurrence + old_len;
1159-
offset_num = offset_ptr - str;
1160-
1180+
str_len_remain = str + str_len - offset_ptr;
11611181
num_replacements_done++;
1162-
if (max_rep != 0 && num_replacements_done == max_rep){
1163-
break;
1164-
}
11651182
}
11661183

11671184
// copy from just after end of last occurrence of to-be-replaced string to end of old string
11681185
if (data != NULL) {
1169-
memcpy(data + replaced_str_index, offset_ptr, str_len - offset_num);
1186+
memcpy(data + replaced_str_index, offset_ptr, str_len_remain);
11701187
}
1171-
replaced_str_index += str_len - offset_num;
1188+
replaced_str_index += str_len_remain;
11721189

11731190
if (data == NULL) {
11741191
// first pass
@@ -1178,6 +1195,7 @@ STATIC mp_obj_t str_replace(uint n_args, const mp_obj_t *args) {
11781195
} else {
11791196
// substr found, allocate new string
11801197
replaced_str = mp_obj_str_builder_start(mp_obj_get_type(args[0]), replaced_str_index, &data);
1198+
assert(data != NULL);
11811199
}
11821200
} else {
11831201
// second pass, we are done

tests/basics/string_replace.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,8 @@
66
print("a".replace("aa", "bb"))
77
print("testingtesting".replace("ing", ""))
88
print("testINGtesting".replace("ing", "ING!"))
9+
10+
print("".replace("", "1"))
11+
print("A".replace("", "1"))
12+
print("AB".replace("", "1"))
13+
print("AB".replace("", "12"))

0 commit comments

Comments
 (0)