Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
98338227
Commit
98338227
authored
Dec 02, 2010
by
Daniel Stutzbach
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Issue9915: speeding up sorting with a key
parent
a0b44b5a
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
240 additions
and
225 deletions
+240
-225
Misc/NEWS
Misc/NEWS
+2
-0
Objects/listobject.c
Objects/listobject.c
+238
-225
No files found.
Misc/NEWS
View file @
98338227
...
...
@@ -10,6 +10,8 @@ What's New in Python 3.2 Beta 1?
Core and Builtins
-----------------
- Issue #9915: Speed up sorting with a key.
- Issue #9333: Expose os.symlink only when the SeCreateSymbolicLinkPrivilege
is held by the user's account, i.e., when the function can actually be used.
...
...
Objects/listobject.c
View file @
98338227
...
...
@@ -940,6 +940,66 @@ reverse_slice(PyObject **lo, PyObject **hi)
* pieces to this algorithm; read listsort.txt for overviews and details.
*/
/* A sortslice contains a pointer to an array of keys and a pointer to
* an array of corresponding values. In other words, keys[i]
* corresponds with values[i]. If values == NULL, then the keys are
* also the values.
*
* Several convenience routines are provided here, so that keys and
* values are always moved in sync.
*/
typedef
struct
{
PyObject
**
keys
;
PyObject
**
values
;
}
sortslice
;
Py_LOCAL_INLINE
(
void
)
sortslice_copy
(
sortslice
*
s1
,
Py_ssize_t
i
,
sortslice
*
s2
,
Py_ssize_t
j
)
{
s1
->
keys
[
i
]
=
s2
->
keys
[
j
];
if
(
s1
->
values
!=
NULL
)
s1
->
values
[
i
]
=
s2
->
values
[
j
];
}
Py_LOCAL_INLINE
(
void
)
sortslice_copy_incr
(
sortslice
*
dst
,
sortslice
*
src
)
{
*
dst
->
keys
++
=
*
src
->
keys
++
;
if
(
dst
->
values
!=
NULL
)
*
dst
->
values
++
=
*
src
->
values
++
;
}
Py_LOCAL_INLINE
(
void
)
sortslice_copy_decr
(
sortslice
*
dst
,
sortslice
*
src
)
{
*
dst
->
keys
--
=
*
src
->
keys
--
;
if
(
dst
->
values
!=
NULL
)
*
dst
->
values
--
=
*
src
->
values
--
;
}
Py_LOCAL_INLINE
(
void
)
sortslice_memcpy
(
sortslice
*
s1
,
Py_ssize_t
i
,
sortslice
*
s2
,
Py_ssize_t
j
,
Py_ssize_t
n
)
{
memcpy
(
&
s1
->
keys
[
i
],
&
s2
->
keys
[
j
],
sizeof
(
PyObject
*
)
*
n
);
if
(
s1
->
values
!=
NULL
)
memcpy
(
&
s1
->
values
[
i
],
&
s2
->
values
[
j
],
sizeof
(
PyObject
*
)
*
n
);
}
Py_LOCAL_INLINE
(
void
)
sortslice_memmove
(
sortslice
*
s1
,
Py_ssize_t
i
,
sortslice
*
s2
,
Py_ssize_t
j
,
Py_ssize_t
n
)
{
memmove
(
&
s1
->
keys
[
i
],
&
s2
->
keys
[
j
],
sizeof
(
PyObject
*
)
*
n
);
if
(
s1
->
values
!=
NULL
)
memmove
(
&
s1
->
values
[
i
],
&
s2
->
values
[
j
],
sizeof
(
PyObject
*
)
*
n
);
}
Py_LOCAL_INLINE
(
void
)
sortslice_advance
(
sortslice
*
slice
,
Py_ssize_t
n
)
{
slice
->
keys
+=
n
;
if
(
slice
->
values
!=
NULL
)
slice
->
values
+=
n
;
}
/* Comparison function: PyObject_RichCompareBool with Py_LT.
* Returns -1 on error, 1 if x < y, 0 if x >= y.
*/
...
...
@@ -965,19 +1025,19 @@ reverse_slice(PyObject **lo, PyObject **hi)
the input (nothing is lost or duplicated).
*/
static
int
binarysort
(
PyObject
**
lo
,
PyObject
**
hi
,
PyObject
**
start
)
binarysort
(
sortslice
lo
,
PyObject
**
hi
,
PyObject
**
start
)
{
register
Py_ssize_t
k
;
register
PyObject
**
l
,
**
p
,
**
r
;
register
PyObject
*
pivot
;
assert
(
lo
<=
start
&&
start
<=
hi
);
assert
(
lo
.
keys
<=
start
&&
start
<=
hi
);
/* assert [lo, start) is sorted */
if
(
lo
==
start
)
if
(
lo
.
keys
==
start
)
++
start
;
for
(;
start
<
hi
;
++
start
)
{
/* set l to where *start belongs */
l
=
lo
;
l
=
lo
.
keys
;
r
=
start
;
pivot
=
*
r
;
/* Invariants:
...
...
@@ -1004,6 +1064,15 @@ binarysort(PyObject **lo, PyObject **hi, PyObject **start)
for
(
p
=
start
;
p
>
l
;
--
p
)
*
p
=
*
(
p
-
1
);
*
l
=
pivot
;
if
(
lo
.
values
!=
NULL
)
{
Py_ssize_t
offset
=
lo
.
values
-
lo
.
keys
;
p
=
start
+
offset
;
pivot
=
*
p
;
l
+=
offset
;
for
(
p
=
start
+
offset
;
p
>
l
;
--
p
)
*
p
=
*
(
p
-
1
);
*
l
=
pivot
;
}
}
return
0
;
...
...
@@ -1272,7 +1341,7 @@ fail:
* a convenient way to pass state around among the helper functions.
*/
struct
s_slice
{
PyObject
**
base
;
sortslice
base
;
Py_ssize_t
len
;
};
...
...
@@ -1286,7 +1355,7 @@ typedef struct s_MergeState {
/* 'a' is temp storage to help with merges. It contains room for
* alloced entries.
*/
PyObject
**
a
;
/* may point to temparray below */
sortslice
a
;
/* may point to temparray below */
Py_ssize_t
alloced
;
/* A stack of n pending runs yet to be merged. Run #i starts at
...
...
@@ -1307,11 +1376,29 @@ typedef struct s_MergeState {
/* Conceptually a MergeState's constructor. */
static
void
merge_init
(
MergeState
*
ms
)
merge_init
(
MergeState
*
ms
,
int
list_size
,
int
has_keyfunc
)
{
assert
(
ms
!=
NULL
);
ms
->
a
=
ms
->
temparray
;
ms
->
alloced
=
MERGESTATE_TEMP_SIZE
;
if
(
has_keyfunc
)
{
/* The temporary space for merging will need at most half the list
* size rounded up. Use the minimum possible space so we can use the
* rest of temparray for other things. In particular, if there is
* enough extra space, listsort() will use it to store the keys.
*/
ms
->
alloced
=
(
list_size
+
1
)
/
2
;
/* ms->alloced describes how many keys will be stored at
ms->temparray, but we also need to store the values. Hence,
ms->alloced is capped at half of MERGESTATE_TEMP_SIZE. */
if
(
MERGESTATE_TEMP_SIZE
/
2
<
ms
->
alloced
)
ms
->
alloced
=
MERGESTATE_TEMP_SIZE
/
2
;
ms
->
a
.
values
=
&
ms
->
temparray
[
ms
->
alloced
];
}
else
{
ms
->
alloced
=
MERGESTATE_TEMP_SIZE
;
ms
->
a
.
values
=
NULL
;
}
ms
->
a
.
keys
=
ms
->
temparray
;
ms
->
n
=
0
;
ms
->
min_gallop
=
MIN_GALLOP
;
}
...
...
@@ -1324,10 +1411,8 @@ static void
merge_freemem
(
MergeState
*
ms
)
{
assert
(
ms
!=
NULL
);
if
(
ms
->
a
!=
ms
->
temparray
)
PyMem_Free
(
ms
->
a
);
ms
->
a
=
ms
->
temparray
;
ms
->
alloced
=
MERGESTATE_TEMP_SIZE
;
if
(
ms
->
a
.
keys
!=
ms
->
temparray
)
PyMem_Free
(
ms
->
a
.
keys
);
}
/* Ensure enough temp memory for 'need' array slots is available.
...
...
@@ -1336,52 +1421,60 @@ merge_freemem(MergeState *ms)
static
int
merge_getmem
(
MergeState
*
ms
,
Py_ssize_t
need
)
{
int
multiplier
;
assert
(
ms
!=
NULL
);
if
(
need
<=
ms
->
alloced
)
return
0
;
multiplier
=
ms
->
a
.
values
!=
NULL
?
2
:
1
;
/* Don't realloc! That can cost cycles to copy the old data, but
* we don't care what's in the block.
*/
merge_freemem
(
ms
);
if
((
size_t
)
need
>
PY_SSIZE_T_MAX
/
sizeof
(
PyObject
*
))
{
if
((
size_t
)
need
>
PY_SSIZE_T_MAX
/
sizeof
(
PyObject
*
)
/
multiplier
)
{
PyErr_NoMemory
();
return
-
1
;
}
ms
->
a
=
(
PyObject
**
)
PyMem_Malloc
(
need
*
sizeof
(
PyObject
*
));
if
(
ms
->
a
)
{
ms
->
a
.
keys
=
(
PyObject
**
)
PyMem_Malloc
(
multiplier
*
need
*
sizeof
(
PyObject
*
));
if
(
ms
->
a
.
keys
!=
NULL
)
{
ms
->
alloced
=
need
;
if
(
ms
->
a
.
values
!=
NULL
)
ms
->
a
.
values
=
&
ms
->
a
.
keys
[
need
];
return
0
;
}
PyErr_NoMemory
();
merge_freemem
(
ms
);
/* reset to sane state */
return
-
1
;
}
#define MERGE_GETMEM(MS, NEED) ((NEED) <= (MS)->alloced ? 0 : \
merge_getmem(MS, NEED))
/* Merge the na elements starting at
pa with the nb elements starting at pb
*
in a stable way, in-place. na and nb must be > 0, and pa + na == pb
.
* Must also have that
*pb < *pa, that pa[na-1] belongs at the end of the
*
merge, and should have na <= nb. See listsort.txt for more info.
*
Return 0 if
successful, -1 if error.
/* Merge the na elements starting at
ssa with the nb elements starting at
*
ssb.keys = ssa.keys + na in a stable way, in-place. na and nb must be > 0
.
* Must also have that
ssa.keys[na-1] belongs at the end of the merge, and
*
should have na <= nb. See listsort.txt for more info. Return 0 if
* successful, -1 if error.
*/
static
Py_ssize_t
merge_lo
(
MergeState
*
ms
,
PyObject
**
p
a
,
Py_ssize_t
na
,
PyObject
**
p
b
,
Py_ssize_t
nb
)
merge_lo
(
MergeState
*
ms
,
sortslice
ss
a
,
Py_ssize_t
na
,
sortslice
ss
b
,
Py_ssize_t
nb
)
{
Py_ssize_t
k
;
PyObject
**
dest
;
sortslice
dest
;
int
result
=
-
1
;
/* guilty until proved innocent */
Py_ssize_t
min_gallop
;
assert
(
ms
&&
pa
&&
pb
&&
na
>
0
&&
nb
>
0
&&
pa
+
na
==
pb
);
assert
(
ms
&&
ssa
.
keys
&&
ssb
.
keys
&&
na
>
0
&&
nb
>
0
);
assert
(
ssa
.
keys
+
na
==
ssb
.
keys
);
if
(
MERGE_GETMEM
(
ms
,
na
)
<
0
)
return
-
1
;
memcpy
(
ms
->
a
,
pa
,
na
*
sizeof
(
PyObject
*
)
);
dest
=
p
a
;
p
a
=
ms
->
a
;
sortslice_memcpy
(
&
ms
->
a
,
0
,
&
ssa
,
0
,
na
);
dest
=
ss
a
;
ss
a
=
ms
->
a
;
*
dest
++
=
*
pb
++
;
sortslice_copy_incr
(
&
dest
,
&
ssb
)
;
--
nb
;
if
(
nb
==
0
)
goto
Succeed
;
...
...
@@ -1398,11 +1491,11 @@ merge_lo(MergeState *ms, PyObject **pa, Py_ssize_t na,
*/
for
(;;)
{
assert
(
na
>
1
&&
nb
>
0
);
k
=
ISLT
(
*
pb
,
*
pa
);
k
=
ISLT
(
ssb
.
keys
[
0
],
ssa
.
keys
[
0
]
);
if
(
k
)
{
if
(
k
<
0
)
goto
Fail
;
*
dest
++
=
*
pb
++
;
sortslice_copy_incr
(
&
dest
,
&
ssb
)
;
++
bcount
;
acount
=
0
;
--
nb
;
...
...
@@ -1412,7 +1505,7 @@ merge_lo(MergeState *ms, PyObject **pa, Py_ssize_t na,
break
;
}
else
{
*
dest
++
=
*
pa
++
;
sortslice_copy_incr
(
&
dest
,
&
ssa
)
;
++
acount
;
bcount
=
0
;
--
na
;
...
...
@@ -1433,14 +1526,14 @@ merge_lo(MergeState *ms, PyObject **pa, Py_ssize_t na,
assert
(
na
>
1
&&
nb
>
0
);
min_gallop
-=
min_gallop
>
1
;
ms
->
min_gallop
=
min_gallop
;
k
=
gallop_right
(
*
pb
,
pa
,
na
,
0
);
k
=
gallop_right
(
ssb
.
keys
[
0
],
ssa
.
keys
,
na
,
0
);
acount
=
k
;
if
(
k
)
{
if
(
k
<
0
)
goto
Fail
;
memcpy
(
dest
,
pa
,
k
*
sizeof
(
PyObject
*
)
);
dest
+=
k
;
pa
+=
k
;
sortslice_memcpy
(
&
dest
,
0
,
&
ssa
,
0
,
k
);
sortslice_advance
(
&
dest
,
k
)
;
sortslice_advance
(
&
ssa
,
k
)
;
na
-=
k
;
if
(
na
==
1
)
goto
CopyB
;
...
...
@@ -1451,24 +1544,24 @@ merge_lo(MergeState *ms, PyObject **pa, Py_ssize_t na,
if
(
na
==
0
)
goto
Succeed
;
}
*
dest
++
=
*
pb
++
;
sortslice_copy_incr
(
&
dest
,
&
ssb
)
;
--
nb
;
if
(
nb
==
0
)
goto
Succeed
;
k
=
gallop_left
(
*
pa
,
pb
,
nb
,
0
);
k
=
gallop_left
(
ssa
.
keys
[
0
],
ssb
.
keys
,
nb
,
0
);
bcount
=
k
;
if
(
k
)
{
if
(
k
<
0
)
goto
Fail
;
memmove
(
dest
,
pb
,
k
*
sizeof
(
PyObject
*
)
);
dest
+=
k
;
pb
+=
k
;
sortslice_memmove
(
&
dest
,
0
,
&
ssb
,
0
,
k
);
sortslice_advance
(
&
dest
,
k
)
;
sortslice_advance
(
&
ssb
,
k
)
;
nb
-=
k
;
if
(
nb
==
0
)
goto
Succeed
;
}
*
dest
++
=
*
pa
++
;
sortslice_copy_incr
(
&
dest
,
&
ssa
)
;
--
na
;
if
(
na
==
1
)
goto
CopyB
;
...
...
@@ -1480,43 +1573,46 @@ Succeed:
result
=
0
;
Fail:
if
(
na
)
memcpy
(
dest
,
pa
,
na
*
sizeof
(
PyObject
*
)
);
sortslice_memcpy
(
&
dest
,
0
,
&
ssa
,
0
,
na
);
return
result
;
CopyB:
assert
(
na
==
1
&&
nb
>
0
);
/* The last element of
p
a belongs at the end of the merge. */
memmove
(
dest
,
pb
,
nb
*
sizeof
(
PyObject
*
)
);
dest
[
nb
]
=
*
pa
;
/* The last element of
ss
a belongs at the end of the merge. */
sortslice_memmove
(
&
dest
,
0
,
&
ssb
,
0
,
nb
);
sortslice_copy
(
&
dest
,
nb
,
&
ssa
,
0
)
;
return
0
;
}
/* Merge the na elements starting at pa with the nb elements starting at
pb
*
in a stable way, in-place. na and nb must be > 0, and pa + na == pb
.
* Must also have that
*pb < *pa, that pa[na-1] belongs at the end of the
*
merge, and should have na >= nb. See listsort.txt for more info.
*
Return 0 if
successful, -1 if error.
/* Merge the na elements starting at pa with the nb elements starting at
*
ssb.keys = ssa.keys + na in a stable way, in-place. na and nb must be > 0
.
* Must also have that
ssa.keys[na-1] belongs at the end of the merge, and
*
should have na >= nb. See listsort.txt for more info. Return 0 if
* successful, -1 if error.
*/
static
Py_ssize_t
merge_hi
(
MergeState
*
ms
,
PyObject
**
pa
,
Py_ssize_t
na
,
PyObject
**
pb
,
Py_ssize_t
nb
)
merge_hi
(
MergeState
*
ms
,
sortslice
ssa
,
Py_ssize_t
na
,
sortslice
ssb
,
Py_ssize_t
nb
)
{
Py_ssize_t
k
;
PyObject
**
dest
;
sortslice
dest
,
basea
,
baseb
;
int
result
=
-
1
;
/* guilty until proved innocent */
PyObject
**
basea
;
PyObject
**
baseb
;
Py_ssize_t
min_gallop
;
assert
(
ms
&&
pa
&&
pb
&&
na
>
0
&&
nb
>
0
&&
pa
+
na
==
pb
);
assert
(
ms
&&
ssa
.
keys
&&
ssb
.
keys
&&
na
>
0
&&
nb
>
0
);
assert
(
ssa
.
keys
+
na
==
ssb
.
keys
);
if
(
MERGE_GETMEM
(
ms
,
nb
)
<
0
)
return
-
1
;
dest
=
pb
+
nb
-
1
;
memcpy
(
ms
->
a
,
pb
,
nb
*
sizeof
(
PyObject
*
));
basea
=
pa
;
dest
=
ssb
;
sortslice_advance
(
&
dest
,
nb
-
1
);
sortslice_memcpy
(
&
ms
->
a
,
0
,
&
ssb
,
0
,
nb
);
basea
=
ssa
;
baseb
=
ms
->
a
;
pb
=
ms
->
a
+
nb
-
1
;
pa
+=
na
-
1
;
ssb
.
keys
=
ms
->
a
.
keys
+
nb
-
1
;
if
(
ssb
.
values
!=
NULL
)
ssb
.
values
=
ms
->
a
.
values
+
nb
-
1
;
sortslice_advance
(
&
ssa
,
na
-
1
);
*
dest
--
=
*
pa
--
;
sortslice_copy_decr
(
&
dest
,
&
ssa
)
;
--
na
;
if
(
na
==
0
)
goto
Succeed
;
...
...
@@ -1533,11 +1629,11 @@ merge_hi(MergeState *ms, PyObject **pa, Py_ssize_t na, PyObject **pb, Py_ssize_t
*/
for
(;;)
{
assert
(
na
>
0
&&
nb
>
1
);
k
=
ISLT
(
*
pb
,
*
pa
);
k
=
ISLT
(
ssb
.
keys
[
0
],
ssa
.
keys
[
0
]
);
if
(
k
)
{
if
(
k
<
0
)
goto
Fail
;
*
dest
--
=
*
pa
--
;
sortslice_copy_decr
(
&
dest
,
&
ssa
)
;
++
acount
;
bcount
=
0
;
--
na
;
...
...
@@ -1547,7 +1643,7 @@ merge_hi(MergeState *ms, PyObject **pa, Py_ssize_t na, PyObject **pb, Py_ssize_t
break
;
}
else
{
*
dest
--
=
*
pb
--
;
sortslice_copy_decr
(
&
dest
,
&
ssb
)
;
++
bcount
;
acount
=
0
;
--
nb
;
...
...
@@ -1568,33 +1664,33 @@ merge_hi(MergeState *ms, PyObject **pa, Py_ssize_t na, PyObject **pb, Py_ssize_t
assert
(
na
>
0
&&
nb
>
1
);
min_gallop
-=
min_gallop
>
1
;
ms
->
min_gallop
=
min_gallop
;
k
=
gallop_right
(
*
pb
,
basea
,
na
,
na
-
1
);
k
=
gallop_right
(
ssb
.
keys
[
0
],
basea
.
keys
,
na
,
na
-
1
);
if
(
k
<
0
)
goto
Fail
;
k
=
na
-
k
;
acount
=
k
;
if
(
k
)
{
dest
-=
k
;
pa
-=
k
;
memmove
(
dest
+
1
,
pa
+
1
,
k
*
sizeof
(
PyObject
*
)
);
sortslice_advance
(
&
dest
,
-
k
)
;
sortslice_advance
(
&
ssa
,
-
k
)
;
sortslice_memmove
(
&
dest
,
1
,
&
ssa
,
1
,
k
);
na
-=
k
;
if
(
na
==
0
)
goto
Succeed
;
}
*
dest
--
=
*
pb
--
;
sortslice_copy_decr
(
&
dest
,
&
ssb
)
;
--
nb
;
if
(
nb
==
1
)
goto
CopyA
;
k
=
gallop_left
(
*
pa
,
baseb
,
nb
,
nb
-
1
);
k
=
gallop_left
(
ssa
.
keys
[
0
],
baseb
.
keys
,
nb
,
nb
-
1
);
if
(
k
<
0
)
goto
Fail
;
k
=
nb
-
k
;
bcount
=
k
;
if
(
k
)
{
dest
-=
k
;
pb
-=
k
;
memcpy
(
dest
+
1
,
pb
+
1
,
k
*
sizeof
(
PyObject
*
)
);
sortslice_advance
(
&
dest
,
-
k
)
;
sortslice_advance
(
&
ssb
,
-
k
)
;
sortslice_memcpy
(
&
dest
,
1
,
&
ssb
,
1
,
k
);
nb
-=
k
;
if
(
nb
==
1
)
goto
CopyA
;
...
...
@@ -1605,7 +1701,7 @@ merge_hi(MergeState *ms, PyObject **pa, Py_ssize_t na, PyObject **pb, Py_ssize_t
if
(
nb
==
0
)
goto
Succeed
;
}
*
dest
--
=
*
pa
--
;
sortslice_copy_decr
(
&
dest
,
&
ssa
)
;
--
na
;
if
(
na
==
0
)
goto
Succeed
;
...
...
@@ -1617,15 +1713,15 @@ Succeed:
result
=
0
;
Fail:
if
(
nb
)
memcpy
(
dest
-
(
nb
-
1
),
baseb
,
nb
*
sizeof
(
PyObject
*
)
);
sortslice_memcpy
(
&
dest
,
-
(
nb
-
1
),
&
baseb
,
0
,
nb
);
return
result
;
CopyA:
assert
(
nb
==
1
&&
na
>
0
);
/* The first element of
p
b belongs at the front of the merge. */
dest
-=
na
;
pa
-=
na
;
memmove
(
dest
+
1
,
pa
+
1
,
na
*
sizeof
(
PyObject
*
)
);
*
dest
=
*
pb
;
/* The first element of
ss
b belongs at the front of the merge. */
sortslice_memmove
(
&
dest
,
1
-
na
,
&
ssa
,
1
-
na
,
na
)
;
sortslice_advance
(
&
dest
,
-
na
)
;
sortslice_advance
(
&
ssa
,
-
na
);
sortslice_copy
(
&
dest
,
0
,
&
ssb
,
0
)
;
return
0
;
}
...
...
@@ -1635,7 +1731,7 @@ CopyA:
static
Py_ssize_t
merge_at
(
MergeState
*
ms
,
Py_ssize_t
i
)
{
PyObject
**
pa
,
**
p
b
;
sortslice
ssa
,
ss
b
;
Py_ssize_t
na
,
nb
;
Py_ssize_t
k
;
...
...
@@ -1644,12 +1740,12 @@ merge_at(MergeState *ms, Py_ssize_t i)
assert
(
i
>=
0
);
assert
(
i
==
ms
->
n
-
2
||
i
==
ms
->
n
-
3
);
p
a
=
ms
->
pending
[
i
].
base
;
ss
a
=
ms
->
pending
[
i
].
base
;
na
=
ms
->
pending
[
i
].
len
;
p
b
=
ms
->
pending
[
i
+
1
].
base
;
ss
b
=
ms
->
pending
[
i
+
1
].
base
;
nb
=
ms
->
pending
[
i
+
1
].
len
;
assert
(
na
>
0
&&
nb
>
0
);
assert
(
pa
+
na
==
pb
);
assert
(
ssa
.
keys
+
na
==
ssb
.
keys
);
/* Record the length of the combined runs; if i is the 3rd-last
* run now, also slide over the last run (which isn't involved
...
...
@@ -1663,10 +1759,10 @@ merge_at(MergeState *ms, Py_ssize_t i)
/* Where does b start in a? Elements in a before that can be
* ignored (already in place).
*/
k
=
gallop_right
(
*
pb
,
pa
,
na
,
0
);
k
=
gallop_right
(
*
ssb
.
keys
,
ssa
.
keys
,
na
,
0
);
if
(
k
<
0
)
return
-
1
;
pa
+=
k
;
sortslice_advance
(
&
ssa
,
k
)
;
na
-=
k
;
if
(
na
==
0
)
return
0
;
...
...
@@ -1674,7 +1770,7 @@ merge_at(MergeState *ms, Py_ssize_t i)
/* Where does a end in b? Elements in b after that can be
* ignored (already in place).
*/
nb
=
gallop_left
(
pa
[
na
-
1
],
pb
,
nb
,
nb
-
1
);
nb
=
gallop_left
(
ssa
.
keys
[
na
-
1
],
ssb
.
keys
,
nb
,
nb
-
1
);
if
(
nb
<=
0
)
return
nb
;
...
...
@@ -1682,9 +1778,9 @@ merge_at(MergeState *ms, Py_ssize_t i)
* min(na, nb) elements.
*/
if
(
na
<=
nb
)
return
merge_lo
(
ms
,
pa
,
na
,
p
b
,
nb
);
return
merge_lo
(
ms
,
ssa
,
na
,
ss
b
,
nb
);
else
return
merge_hi
(
ms
,
pa
,
na
,
p
b
,
nb
);
return
merge_hi
(
ms
,
ssa
,
na
,
ss
b
,
nb
);
}
/* Examine the stack of runs waiting to be merged, merging adjacent runs
...
...
@@ -1765,103 +1861,12 @@ merge_compute_minrun(Py_ssize_t n)
return
n
+
r
;
}
/* Special wrapper to support stable sorting using the decorate-sort-undecorate
pattern. Holds a key which is used for comparisons and the original record
which is returned during the undecorate phase. By exposing only the key
during comparisons, the underlying sort stability characteristics are left
unchanged. Also, the comparison function will only see the key instead of
a full record. */
typedef
struct
{
PyObject_HEAD
PyObject
*
key
;
PyObject
*
value
;
}
sortwrapperobject
;
PyDoc_STRVAR
(
sortwrapper_doc
,
"Object wrapper with a custom sort key."
);
static
PyObject
*
sortwrapper_richcompare
(
sortwrapperobject
*
,
sortwrapperobject
*
,
int
);
static
void
sortwrapper_dealloc
(
sortwrapperobject
*
);
PyTypeObject
PySortWrapper_Type
=
{
PyVarObject_HEAD_INIT
(
&
PyType_Type
,
0
)
"sortwrapper"
,
/* tp_name */
sizeof
(
sortwrapperobject
),
/* tp_basicsize */
0
,
/* tp_itemsize */
/* methods */
(
destructor
)
sortwrapper_dealloc
,
/* tp_dealloc */
0
,
/* tp_print */
0
,
/* tp_getattr */
0
,
/* tp_setattr */
0
,
/* tp_reserved */
0
,
/* tp_repr */
0
,
/* tp_as_number */
0
,
/* tp_as_sequence */
0
,
/* tp_as_mapping */
0
,
/* tp_hash */
0
,
/* tp_call */
0
,
/* tp_str */
PyObject_GenericGetAttr
,
/* tp_getattro */
0
,
/* tp_setattro */
0
,
/* tp_as_buffer */
Py_TPFLAGS_DEFAULT
,
/* tp_flags */
sortwrapper_doc
,
/* tp_doc */
0
,
/* tp_traverse */
0
,
/* tp_clear */
(
richcmpfunc
)
sortwrapper_richcompare
,
/* tp_richcompare */
};
static
PyObject
*
sortwrapper_richcompare
(
sortwrapperobject
*
a
,
sortwrapperobject
*
b
,
int
op
)
{
if
(
!
PyObject_TypeCheck
(
b
,
&
PySortWrapper_Type
))
{
PyErr_SetString
(
PyExc_TypeError
,
"expected a sortwrapperobject"
);
return
NULL
;
}
return
PyObject_RichCompare
(
a
->
key
,
b
->
key
,
op
);
}
static
void
sortwrapper_dealloc
(
sortwrapperobject
*
so
)
{
Py_XDECREF
(
so
->
key
);
Py_XDECREF
(
so
->
value
);
PyObject_Del
(
so
);
}
/* Returns a new reference to a sortwrapper.
Consumes the references to the two underlying objects. */
static
PyObject
*
build_sortwrapper
(
PyObject
*
key
,
PyObject
*
value
)
reverse_sortslice
(
sortslice
*
s
,
Py_ssize_t
n
)
{
sortwrapperobject
*
so
;
so
=
PyObject_New
(
sortwrapperobject
,
&
PySortWrapper_Type
);
if
(
so
==
NULL
)
return
NULL
;
so
->
key
=
key
;
so
->
value
=
value
;
return
(
PyObject
*
)
so
;
}
/* Returns a new reference to the value underlying the wrapper. */
static
PyObject
*
sortwrapper_getvalue
(
PyObject
*
so
)
{
PyObject
*
value
;
if
(
!
PyObject_TypeCheck
(
so
,
&
PySortWrapper_Type
))
{
PyErr_SetString
(
PyExc_TypeError
,
"expected a sortwrapperobject"
);
return
NULL
;
}
value
=
((
sortwrapperobject
*
)
so
)
->
value
;
Py_INCREF
(
value
);
return
value
;
reverse_slice
(
s
->
keys
,
&
s
->
keys
[
n
]);
if
(
s
->
values
!=
NULL
)
reverse_slice
(
s
->
values
,
&
s
->
values
[
n
]);
}
/* An adaptive, stable, natural mergesort. See listsort.txt.
...
...
@@ -1873,9 +1878,9 @@ static PyObject *
listsort
(
PyListObject
*
self
,
PyObject
*
args
,
PyObject
*
kwds
)
{
MergeState
ms
;
PyObject
**
lo
,
**
hi
;
Py_ssize_t
nremaining
;
Py_ssize_t
minrun
;
sortslice
lo
;
Py_ssize_t
saved_ob_size
,
saved_allocated
;
PyObject
**
saved_ob_item
;
PyObject
**
final_ob_item
;
...
...
@@ -1883,8 +1888,8 @@ listsort(PyListObject *self, PyObject *args, PyObject *kwds)
int
reverse
=
0
;
PyObject
*
keyfunc
=
NULL
;
Py_ssize_t
i
;
PyObject
*
key
,
*
value
,
*
kvpair
;
static
char
*
kwlist
[]
=
{
"key"
,
"reverse"
,
0
};
PyObject
**
keys
;
assert
(
self
!=
NULL
);
assert
(
PyList_Check
(
self
));
...
...
@@ -1913,28 +1918,36 @@ listsort(PyListObject *self, PyObject *args, PyObject *kwds)
self
->
ob_item
=
NULL
;
self
->
allocated
=
-
1
;
/* any operation will reset it to >= 0 */
if
(
keyfunc
!=
NULL
)
{
for
(
i
=
0
;
i
<
saved_ob_size
;
i
++
)
{
value
=
saved_ob_item
[
i
];
key
=
PyObject_CallFunctionObjArgs
(
keyfunc
,
value
,
NULL
);
if
(
key
==
NULL
)
{
for
(
i
=
i
-
1
;
i
>=
0
;
i
--
)
{
kvpair
=
saved_ob_item
[
i
];
value
=
sortwrapper_getvalue
(
kvpair
);
saved_ob_item
[
i
]
=
value
;
Py_DECREF
(
kvpair
);
}
goto
dsu_fail
;
if
(
keyfunc
==
NULL
)
{
keys
=
NULL
;
lo
.
keys
=
saved_ob_item
;
lo
.
values
=
NULL
;
}
else
{
if
(
saved_ob_size
<
MERGESTATE_TEMP_SIZE
/
2
)
/* Leverage stack space we allocated but won't otherwise use */
keys
=
&
ms
.
temparray
[
saved_ob_size
+
1
];
else
{
keys
=
PyMem_MALLOC
(
sizeof
(
PyObject
*
)
*
saved_ob_size
);
if
(
keys
==
NULL
)
return
NULL
;
}
for
(
i
=
0
;
i
<
saved_ob_size
;
i
++
)
{
keys
[
i
]
=
PyObject_CallFunctionObjArgs
(
keyfunc
,
saved_ob_item
[
i
],
NULL
);
if
(
keys
[
i
]
==
NULL
)
{
for
(
i
=
i
-
1
;
i
>=
0
;
i
--
)
Py_DECREF
(
keys
[
i
]);
goto
keyfunc_fail
;
}
kvpair
=
build_sortwrapper
(
key
,
value
);
if
(
kvpair
==
NULL
)
goto
dsu_fail
;
saved_ob_item
[
i
]
=
kvpair
;
}
lo
.
keys
=
keys
;
lo
.
values
=
saved_ob_item
;
}
merge_init
(
&
ms
);
merge_init
(
&
ms
,
saved_ob_size
,
keys
!=
NULL
);
nremaining
=
saved_ob_size
;
if
(
nremaining
<
2
)
...
...
@@ -1942,30 +1955,31 @@ listsort(PyListObject *self, PyObject *args, PyObject *kwds)
/* Reverse sort stability achieved by initially reversing the list,
applying a stable forward sort, then reversing the final result. */
if
(
reverse
)
reverse_slice
(
saved_ob_item
,
saved_ob_item
+
saved_ob_size
);
if
(
reverse
)
{
if
(
keys
!=
NULL
)
reverse_slice
(
&
keys
[
0
],
&
keys
[
saved_ob_size
]);
reverse_slice
(
&
saved_ob_item
[
0
],
&
saved_ob_item
[
saved_ob_size
]);
}
/* March over the array once, left to right, finding natural runs,
* and extending short natural runs to minrun elements.
*/
lo
=
saved_ob_item
;
hi
=
lo
+
nremaining
;
minrun
=
merge_compute_minrun
(
nremaining
);
do
{
int
descending
;
Py_ssize_t
n
;
/* Identify next run. */
n
=
count_run
(
lo
,
hi
,
&
descending
);
n
=
count_run
(
lo
.
keys
,
lo
.
keys
+
nremaining
,
&
descending
);
if
(
n
<
0
)
goto
fail
;
if
(
descending
)
reverse_s
lice
(
lo
,
lo
+
n
);
reverse_s
ortslice
(
&
lo
,
n
);
/* If short, extend to min(minrun, nremaining). */
if
(
n
<
minrun
)
{
const
Py_ssize_t
force
=
nremaining
<=
minrun
?
nremaining
:
minrun
;
if
(
binarysort
(
lo
,
lo
+
force
,
lo
+
n
)
<
0
)
if
(
binarysort
(
lo
,
lo
.
keys
+
force
,
lo
.
keys
+
n
)
<
0
)
goto
fail
;
n
=
force
;
}
...
...
@@ -1977,27 +1991,27 @@ listsort(PyListObject *self, PyObject *args, PyObject *kwds)
if
(
merge_collapse
(
&
ms
)
<
0
)
goto
fail
;
/* Advance to find next run. */
lo
+=
n
;
sortslice_advance
(
&
lo
,
n
)
;
nremaining
-=
n
;
}
while
(
nremaining
);
assert
(
lo
==
hi
);
if
(
merge_force_collapse
(
&
ms
)
<
0
)
goto
fail
;
assert
(
ms
.
n
==
1
);
assert
(
ms
.
pending
[
0
].
base
==
saved_ob_item
);
assert
(
keys
==
NULL
?
ms
.
pending
[
0
].
base
.
keys
==
saved_ob_item
:
ms
.
pending
[
0
].
base
.
keys
==
&
keys
[
0
]);
assert
(
ms
.
pending
[
0
].
len
==
saved_ob_size
);
lo
=
ms
.
pending
[
0
].
base
;
succeed:
result
=
Py_None
;
fail:
if
(
keyfunc
!=
NULL
)
{
for
(
i
=
0
;
i
<
saved_ob_size
;
i
++
)
{
kvpair
=
saved_ob_item
[
i
];
value
=
sortwrapper_getvalue
(
kvpair
);
saved_ob_item
[
i
]
=
value
;
Py_DECREF
(
kvpair
);
}
if
(
keys
!=
NULL
)
{
for
(
i
=
0
;
i
<
saved_ob_size
;
i
++
)
Py_DECREF
(
keys
[
i
]);
if
(
keys
!=
&
ms
.
temparray
[
saved_ob_size
+
1
])
PyMem_FREE
(
keys
);
}
if
(
self
->
allocated
!=
-
1
&&
result
!=
NULL
)
{
...
...
@@ -2013,7 +2027,7 @@ fail:
merge_freemem
(
&
ms
);
dsu
_fail:
keyfunc
_fail:
final_ob_item
=
self
->
ob_item
;
i
=
Py_SIZE
(
self
);
Py_SIZE
(
self
)
=
saved_ob_size
;
...
...
@@ -2862,4 +2876,3 @@ listreviter_len(listreviterobject *it)
len
=
0
;
return
PyLong_FromSsize_t
(
len
);
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment