Skip to content

Commit f71ec5a

Browse files
committed
Bug #1515471: string.replace() accepts character buffers again.
Pass the char* and size around rather than PyObject's.
1 parent 07aadb1 commit f71ec5a

3 files changed

Lines changed: 60 additions & 73 deletions

File tree

Lib/test/string_tests.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ def test_count(self):
147147
else:
148148
r2, rem = len(i)+1, 0
149149
if rem or r1 != r2:
150-
self.assertEqual(rem, 0)
151-
self.assertEqual(r1, r2)
150+
self.assertEqual(rem, 0, '%s != 0 for %s' % (rem, i))
151+
self.assertEqual(r1, r2, '%s != %s for %s' % (r1, r2, i))
152152

153153
def test_find(self):
154154
self.checkequal(0, 'abcdefghiabc', 'find', 'abc')
@@ -636,6 +636,11 @@ def test_replace(self):
636636
EQ("bobobXbobob", "bobobobXbobobob", "replace", "bobob", "bob")
637637
EQ("BOBOBOB", "BOBOBOB", "replace", "bob", "bobby")
638638

639+
ba = buffer('a')
640+
bb = buffer('b')
641+
EQ("bbc", "abc", "replace", ba, bb)
642+
EQ("aac", "abc", "replace", bb, ba)
643+
639644
#
640645
self.checkequal('one@two!three!', 'one!two!three!', 'replace', '!', '@', 1)
641646
self.checkequal('onetwothree', 'one!two!three!', 'replace', '!', '')

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ What's New in Python 2.5 beta 3?
1212
Core and builtins
1313
-----------------
1414

15+
- Bug #1515471: string.replace() accepts character buffers again.
16+
1517
- Add PyErr_WarnEx() so C code can pass the stacklevel to warnings.warn().
1618
This provides the proper warning for struct.pack().
1719
PyErr_Warn() is now deprecated in favor of PyErr_WarnEx().

Objects/stringobject.c

Lines changed: 51 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -2464,11 +2464,11 @@ return_self(PyStringObject *self)
24642464
}
24652465

24662466
Py_LOCAL_INLINE(Py_ssize_t)
2467-
countchar(char *target, int target_len, char c, Py_ssize_t maxcount)
2467+
countchar(const char *target, int target_len, char c, Py_ssize_t maxcount)
24682468
{
24692469
Py_ssize_t count=0;
2470-
char *start=target;
2471-
char *end=target+target_len;
2470+
const char *start=target;
2471+
const char *end=target+target_len;
24722472

24732473
while ( (start=findchar(start, end-start, c)) != NULL ) {
24742474
count++;
@@ -2480,8 +2480,8 @@ countchar(char *target, int target_len, char c, Py_ssize_t maxcount)
24802480
}
24812481

24822482
Py_LOCAL(Py_ssize_t)
2483-
findstring(char *target, Py_ssize_t target_len,
2484-
char *pattern, Py_ssize_t pattern_len,
2483+
findstring(const char *target, Py_ssize_t target_len,
2484+
const char *pattern, Py_ssize_t pattern_len,
24852485
Py_ssize_t start,
24862486
Py_ssize_t end,
24872487
int direction)
@@ -2518,8 +2518,8 @@ findstring(char *target, Py_ssize_t target_len,
25182518
}
25192519

25202520
Py_LOCAL_INLINE(Py_ssize_t)
2521-
countstring(char *target, Py_ssize_t target_len,
2522-
char *pattern, Py_ssize_t pattern_len,
2521+
countstring(const char *target, Py_ssize_t target_len,
2522+
const char *pattern, Py_ssize_t pattern_len,
25232523
Py_ssize_t start,
25242524
Py_ssize_t end,
25252525
int direction, Py_ssize_t maxcount)
@@ -2572,16 +2572,15 @@ countstring(char *target, Py_ssize_t target_len,
25722572
/* len(self)>=1, from="", len(to)>=1, maxcount>=1 */
25732573
Py_LOCAL(PyStringObject *)
25742574
replace_interleave(PyStringObject *self,
2575-
PyStringObject *to,
2575+
const char *to_s, Py_ssize_t to_len,
25762576
Py_ssize_t maxcount)
25772577
{
2578-
char *self_s, *to_s, *result_s;
2579-
Py_ssize_t self_len, to_len, result_len;
2578+
char *self_s, *result_s;
2579+
Py_ssize_t self_len, result_len;
25802580
Py_ssize_t count, i, product;
25812581
PyStringObject *result;
25822582

25832583
self_len = PyString_GET_SIZE(self);
2584-
to_len = PyString_GET_SIZE(to);
25852584

25862585
/* 1 at the end plus 1 after every character */
25872586
count = self_len+1;
@@ -2608,8 +2607,6 @@ replace_interleave(PyStringObject *self,
26082607
return NULL;
26092608

26102609
self_s = PyString_AS_STRING(self);
2611-
to_s = PyString_AS_STRING(to);
2612-
to_len = PyString_GET_SIZE(to);
26132610
result_s = PyString_AS_STRING(result);
26142611

26152612
/* TODO: special case single character, which doesn't need memcpy */
@@ -2677,18 +2674,17 @@ replace_delete_single_character(PyStringObject *self,
26772674
/* len(self)>=1, len(from)>=2, to="", maxcount>=1 */
26782675

26792676
Py_LOCAL(PyStringObject *)
2680-
replace_delete_substring(PyStringObject *self, PyStringObject *from,
2677+
replace_delete_substring(PyStringObject *self,
2678+
const char *from_s, Py_ssize_t from_len,
26812679
Py_ssize_t maxcount) {
2682-
char *self_s, *from_s, *result_s;
2680+
char *self_s, *result_s;
26832681
char *start, *next, *end;
2684-
Py_ssize_t self_len, from_len, result_len;
2682+
Py_ssize_t self_len, result_len;
26852683
Py_ssize_t count, offset;
26862684
PyStringObject *result;
26872685

26882686
self_len = PyString_GET_SIZE(self);
26892687
self_s = PyString_AS_STRING(self);
2690-
from_len = PyString_GET_SIZE(from);
2691-
from_s = PyString_AS_STRING(from);
26922688

26932689
count = countstring(self_s, self_len,
26942690
from_s, from_len,
@@ -2776,24 +2772,20 @@ replace_single_character_in_place(PyStringObject *self,
27762772
/* len(self)>=1, len(from)==len(to)>=2, maxcount>=1 */
27772773
Py_LOCAL(PyStringObject *)
27782774
replace_substring_in_place(PyStringObject *self,
2779-
PyStringObject *from,
2780-
PyStringObject *to,
2775+
const char *from_s, Py_ssize_t from_len,
2776+
const char *to_s, Py_ssize_t to_len,
27812777
Py_ssize_t maxcount)
27822778
{
27832779
char *result_s, *start, *end;
2784-
char *self_s, *from_s, *to_s;
2785-
Py_ssize_t self_len, from_len, offset;
2780+
char *self_s;
2781+
Py_ssize_t self_len, offset;
27862782
PyStringObject *result;
27872783

27882784
/* The result string will be the same size */
27892785

27902786
self_s = PyString_AS_STRING(self);
27912787
self_len = PyString_GET_SIZE(self);
2792-
2793-
from_s = PyString_AS_STRING(from);
2794-
from_len = PyString_GET_SIZE(from);
2795-
to_s = PyString_AS_STRING(to);
2796-
2788+
27972789
offset = findstring(self_s, self_len,
27982790
from_s, from_len,
27992791
0, self_len, FORWARD);
@@ -2810,7 +2802,6 @@ replace_substring_in_place(PyStringObject *self,
28102802
result_s = PyString_AS_STRING(result);
28112803
Py_MEMCPY(result_s, self_s, self_len);
28122804

2813-
28142805
/* change everything in-place, starting with this one */
28152806
start = result_s + offset;
28162807
Py_MEMCPY(start, to_s, from_len);
@@ -2834,12 +2825,12 @@ replace_substring_in_place(PyStringObject *self,
28342825
Py_LOCAL(PyStringObject *)
28352826
replace_single_character(PyStringObject *self,
28362827
char from_c,
2837-
PyStringObject *to,
2828+
const char *to_s, Py_ssize_t to_len,
28382829
Py_ssize_t maxcount)
28392830
{
2840-
char *self_s, *to_s, *result_s;
2831+
char *self_s, *result_s;
28412832
char *start, *next, *end;
2842-
Py_ssize_t self_len, to_len, result_len;
2833+
Py_ssize_t self_len, result_len;
28432834
Py_ssize_t count, product;
28442835
PyStringObject *result;
28452836

@@ -2852,10 +2843,7 @@ replace_single_character(PyStringObject *self,
28522843
/* no matches, return unchanged */
28532844
return return_self(self);
28542845
}
2855-
2856-
to_s = PyString_AS_STRING(to);
2857-
to_len = PyString_GET_SIZE(to);
2858-
2846+
28592847
/* use the difference between current and new, hence the "-1" */
28602848
/* result_len = self_len + count * (to_len-1) */
28612849
product = count * (to_len-1);
@@ -2904,31 +2892,26 @@ replace_single_character(PyStringObject *self,
29042892
/* len(self)>=1, len(from)>=2, len(to)>=2, maxcount>=1 */
29052893
Py_LOCAL(PyStringObject *)
29062894
replace_substring(PyStringObject *self,
2907-
PyStringObject *from,
2908-
PyStringObject *to,
2895+
const char *from_s, Py_ssize_t from_len,
2896+
const char *to_s, Py_ssize_t to_len,
29092897
Py_ssize_t maxcount) {
2910-
char *self_s, *from_s, *to_s, *result_s;
2898+
char *self_s, *result_s;
29112899
char *start, *next, *end;
2912-
Py_ssize_t self_len, from_len, to_len, result_len;
2900+
Py_ssize_t self_len, result_len;
29132901
Py_ssize_t count, offset, product;
29142902
PyStringObject *result;
29152903

29162904
self_s = PyString_AS_STRING(self);
29172905
self_len = PyString_GET_SIZE(self);
2918-
from_s = PyString_AS_STRING(from);
2919-
from_len = PyString_GET_SIZE(from);
2920-
2906+
29212907
count = countstring(self_s, self_len,
29222908
from_s, from_len,
29232909
0, self_len, FORWARD, maxcount);
29242910
if (count == 0) {
29252911
/* no matches, return unchanged */
29262912
return return_self(self);
29272913
}
2928-
2929-
to_s = PyString_AS_STRING(to);
2930-
to_len = PyString_GET_SIZE(to);
2931-
2914+
29322915
/* Check for overflow */
29332916
/* result_len = self_len + count * (to_len-from_len) */
29342917
product = count * (to_len-from_len);
@@ -2979,22 +2962,17 @@ replace_substring(PyStringObject *self,
29792962

29802963
Py_LOCAL(PyStringObject *)
29812964
replace(PyStringObject *self,
2982-
PyStringObject *from,
2983-
PyStringObject *to,
2965+
const char *from_s, Py_ssize_t from_len,
2966+
const char *to_s, Py_ssize_t to_len,
29842967
Py_ssize_t maxcount)
29852968
{
2986-
Py_ssize_t from_len, to_len;
2987-
29882969
if (maxcount < 0) {
29892970
maxcount = PY_SSIZE_T_MAX;
29902971
} else if (maxcount == 0 || PyString_GET_SIZE(self) == 0) {
29912972
/* nothing to do; return the original string */
29922973
return return_self(self);
29932974
}
2994-
2995-
from_len = PyString_GET_SIZE(from);
2996-
to_len = PyString_GET_SIZE(to);
2997-
2975+
29982976
if (maxcount == 0 ||
29992977
(from_len == 0 && to_len == 0)) {
30002978
/* nothing to do; return the original string */
@@ -3007,7 +2985,7 @@ replace(PyStringObject *self,
30072985
/* insert the 'to' string everywhere. */
30082986
/* >>> "Python".replace("", ".") */
30092987
/* '.P.y.t.h.o.n.' */
3010-
return replace_interleave(self, to, maxcount);
2988+
return replace_interleave(self, to_s, to_len, maxcount);
30112989
}
30122990

30132991
/* Except for "".replace("", "A") == "A" there is no way beyond this */
@@ -3021,9 +2999,9 @@ replace(PyStringObject *self,
30212999
/* delete all occurances of 'from' string */
30223000
if (from_len == 1) {
30233001
return replace_delete_single_character(
3024-
self, PyString_AS_STRING(from)[0], maxcount);
3002+
self, from_s[0], maxcount);
30253003
} else {
3026-
return replace_delete_substring(self, from, maxcount);
3004+
return replace_delete_substring(self, from_s, from_len, maxcount);
30273005
}
30283006
}
30293007

@@ -3033,22 +3011,22 @@ replace(PyStringObject *self,
30333011
if (from_len == 1) {
30343012
return replace_single_character_in_place(
30353013
self,
3036-
PyString_AS_STRING(from)[0],
3037-
PyString_AS_STRING(to)[0],
3014+
from_s[0],
3015+
to_s[0],
30383016
maxcount);
30393017
} else {
30403018
return replace_substring_in_place(
3041-
self, from, to, maxcount);
3019+
self, from_s, from_len, to_s, to_len, maxcount);
30423020
}
30433021
}
30443022

30453023
/* Otherwise use the more generic algorithms */
30463024
if (from_len == 1) {
3047-
return replace_single_character(self, PyString_AS_STRING(from)[0],
3048-
to, maxcount);
3025+
return replace_single_character(self, from_s[0],
3026+
to_s, to_len, maxcount);
30493027
} else {
30503028
/* len('from')>=2, len('to')>=1 */
3051-
return replace_substring(self, from, to, maxcount);
3029+
return replace_substring(self, from_s, from_len, to_s, to_len, maxcount);
30523030
}
30533031
}
30543032

@@ -3064,37 +3042,39 @@ string_replace(PyStringObject *self, PyObject *args)
30643042
{
30653043
Py_ssize_t count = -1;
30663044
PyObject *from, *to;
3067-
const char *tmp_s;
3068-
Py_ssize_t tmp_len;
3045+
const char *from_s, *to_s;
3046+
Py_ssize_t from_len, to_len;
30693047

30703048
if (!PyArg_ParseTuple(args, "OO|n:replace", &from, &to, &count))
30713049
return NULL;
30723050

30733051
if (PyString_Check(from)) {
3074-
/* Can this be made a '!check' after the Unicode check? */
3052+
from_s = PyString_AS_STRING(from);
3053+
from_len = PyString_GET_SIZE(from);
30753054
}
30763055
#ifdef Py_USING_UNICODE
30773056
if (PyUnicode_Check(from))
30783057
return PyUnicode_Replace((PyObject *)self,
30793058
from, to, count);
30803059
#endif
3081-
else if (PyObject_AsCharBuffer(from, &tmp_s, &tmp_len))
3060+
else if (PyObject_AsCharBuffer(from, &from_s, &from_len))
30823061
return NULL;
30833062

30843063
if (PyString_Check(to)) {
3085-
/* Can this be made a '!check' after the Unicode check? */
3064+
to_s = PyString_AS_STRING(to);
3065+
to_len = PyString_GET_SIZE(to);
30863066
}
30873067
#ifdef Py_USING_UNICODE
30883068
else if (PyUnicode_Check(to))
30893069
return PyUnicode_Replace((PyObject *)self,
30903070
from, to, count);
30913071
#endif
3092-
else if (PyObject_AsCharBuffer(to, &tmp_s, &tmp_len))
3072+
else if (PyObject_AsCharBuffer(to, &to_s, &to_len))
30933073
return NULL;
30943074

30953075
return (PyObject *)replace((PyStringObject *) self,
3096-
(PyStringObject *) from,
3097-
(PyStringObject *) to, count);
3076+
from_s, from_len,
3077+
to_s, to_len, count);
30983078
}
30993079

31003080
/** End DALKE **/

0 commit comments

Comments
 (0)