Add Rust accelerator module for bisect.

This commit is contained in:
jfh committed 2021-05-30 00:31:23 +03:00
1 parent 658d175e5b
commit 95ce95c9f0
4 files changed
+169 -18

No files matched your search

+4 -5
View File
@@ -19,8 +19,6 @@ def insort_right(a, x, lo=0, hi=None):
else: lo = mid+1
a.insert(lo, x)
insort = insort_right # backward compatibility
def bisect_right(a, x, lo=0, hi=None):
"""Return the index where to insert item x in list a, assuming a is sorted.
@@ -42,8 +40,6 @@ def bisect_right(a, x, lo=0, hi=None):
else: lo = mid+1
return lo
bisect = bisect_right # backward compatibility
def insort_left(a, x, lo=0, hi=None):
"""Insert item x in list a, and keep it sorted assuming a is sorted.
@@ -85,8 +81,11 @@ def bisect_left(a, x, lo=0, hi=None):
else: hi = mid
return lo
# Overwrite above definitions with a fast C implementation
# Overwrite above definitions with a fast Rust implementation
try:
from _bisect import *
except ImportError:
pass
bisect = bisect_right # backward compatibility
insort = insort_right # backward compatibility
+9 -13
View File
@@ -4,7 +4,7 @@ from test import support
from collections import UserList
py_bisect = support.import_fresh_module('bisect', blocked=['_bisect'])
c_bisect = None
rust_bisect = support.import_fresh_module('bisect', fresh=['bisect'])
class Range(object):
"""A trivial range()-like object that has an insert() method."""
@@ -202,9 +202,8 @@ class TestBisect:
class TestBisectPython(TestBisect, unittest.TestCase):
module = py_bisect
@unittest.skip("TODO: RUSTPYTHON, _bisect module")
class TestBisectC(TestBisect, unittest.TestCase):
module = c_bisect
class TestBisectRust(TestBisect, unittest.TestCase):
module = rust_bisect
#==============================================================================
@@ -238,9 +237,8 @@ class TestInsort:
class TestInsortPython(TestInsort, unittest.TestCase):
module = py_bisect
@unittest.skip("TODO: RUSTPYTHON, _bisect module")
class TestInsortC(TestInsort, unittest.TestCase):
module = c_bisect
class TestInsortRust(TestInsort, unittest.TestCase):
module = rust_bisect
#==============================================================================
@@ -294,9 +292,8 @@ class TestErrorHandling:
class TestErrorHandlingPython(TestErrorHandling, unittest.TestCase):
module = py_bisect
@unittest.skip("TODO: RUSTPYTHON, _bisect module")
class TestErrorHandlingC(TestErrorHandling, unittest.TestCase):
module = c_bisect
class TestErrorHandlingRust(TestErrorHandling, unittest.TestCase):
module = rust_bisect
#==============================================================================
@@ -322,9 +319,8 @@ class TestDocExample:
class TestDocExamplePython(TestDocExample, unittest.TestCase):
module = py_bisect
@unittest.skip("TODO: RUSTPYTHON, _bisect module")
class TestDocExampleC(TestDocExample, unittest.TestCase):
module = c_bisect
class TestDocExampleRust(TestDocExample, unittest.TestCase):
module = rust_bisect
#------------------------------------------------------------------------------
+154
View File
@@ -0,0 +1,154 @@
pub(crate) use _bisect::make_module;
#[pymodule]
mod _bisect {
use std::convert::TryFrom;
use crate::function::OptionalArg;
use crate::slots::PyComparisonOp::Lt;
use crate::vm::VirtualMachine;
use crate::{ItemProtocol, PyObjectRef, PyResult};
use num_traits::ToPrimitive;
#[derive(FromArgs)]
struct BisectArgs {
#[pyarg(any, name = "a")]
a: PyObjectRef,
#[pyarg(any, name = "x")]
x: PyObjectRef,
#[pyarg(any, optional, name = "lo")]
lo: OptionalArg<PyObjectRef>,
#[pyarg(any, optional, name = "hi")]
hi: OptionalArg<PyObjectRef>,
}
// Handles objects that implement __index__ and makes sure index fits in needed isize.
#[inline]
fn handle_default(
arg: OptionalArg<PyObjectRef>,
default: Option<isize>,
vm: &VirtualMachine,
) -> PyResult<Option<isize>> {
Ok(match arg {
OptionalArg::Present(v) => {
Some(vm.to_index(&v)?.as_bigint().to_isize().ok_or_else(|| {
vm.new_index_error("cannot fit 'int' into an index-sized integer".to_owned())
})?)
}
OptionalArg::Missing => default,
})
}
// Handles defaults for lo, hi.
//
// - lo must be >= 0 with a default of 0.
// - hi, while it could be negative, defaults to `0` and the same effect is achieved
// (while loop isn't entered); this way we keep it a usize (and, if my understanding
// is correct, issue 13496 is handled). Its default value is set to the length of the
// input sequence.
#[inline]
fn as_usize(
lo: OptionalArg<PyObjectRef>,
hi: OptionalArg<PyObjectRef>,
seq_len: usize,
vm: &VirtualMachine,
) -> PyResult<(usize, usize)> {
// We only deal with positives for lo, try_from can't fail.
// Default is always a Some so we can safely unwrap.
let lo = handle_default(lo, Some(0), vm)?
.map(|value| {
if value < 0 {
return Err(vm.new_value_error("lo must be non-negative".to_owned()));
}
Ok(usize::try_from(value).unwrap())
})
.unwrap()?;
let hi = handle_default(hi, None, vm)?
.map(|value| {
if value < 0 {
0
} else {
// Can't fail.
usize::try_from(value).unwrap()
}
})
.unwrap_or(seq_len);
Ok((lo, hi))
}
#[inline]
fn bisect_left_impl(
BisectArgs { a, x, lo, hi }: BisectArgs,
vm: &VirtualMachine,
) -> PyResult<usize> {
let (mut lo, mut hi) = as_usize(lo, hi, vm.obj_len(&a)?, vm)?;
while lo < hi {
// Handles issue 13496.
let mid = (lo + hi) / 2;
if vm.bool_cmp(&a.get_item(mid, vm)?, &x, Lt)? {
lo = mid + 1;
} else {
hi = mid;
}
}
Ok(lo)
}
#[inline]
fn bisect_right_impl(
BisectArgs { a, x, lo, hi }: BisectArgs,
vm: &VirtualMachine,
) -> PyResult<usize> {
let (mut lo, mut hi) = as_usize(lo, hi, vm.obj_len(&a)?, vm)?;
while lo < hi {
// Handles issue 13496.
let mid = (lo + hi) / 2;
if vm.bool_cmp(&x, &a.get_item(mid, vm)?, Lt)? {
hi = mid;
} else {
lo = mid + 1;
}
}
Ok(lo)
}
#[pyfunction]
fn bisect_left(args: BisectArgs, vm: &VirtualMachine) -> PyResult<usize> {
bisect_left_impl(args, vm)
}
#[pyfunction]
fn bisect_right(args: BisectArgs, vm: &VirtualMachine) -> PyResult<usize> {
bisect_right_impl(args, vm)
}
#[pyfunction]
fn insort_left(BisectArgs { a, x, lo, hi }: BisectArgs, vm: &VirtualMachine) -> PyResult {
let index = bisect_left_impl(
BisectArgs {
a: a.clone(),
x: x.clone(),
lo,
hi,
},
vm,
)?;
vm.call_method(&a, "insert", (index, x))
}
#[pyfunction]
fn insort_right(BisectArgs { a, x, lo, hi }: BisectArgs, vm: &VirtualMachine) -> PyResult {
let index = bisect_right_impl(
BisectArgs {
a: a.clone(),
x: x.clone(),
lo,
hi,
},
vm,
)?;
vm.call_method(&a, "insert", (index, x))
}
}
+2
View File
@@ -8,6 +8,7 @@ pub mod array;
pub(crate) mod ast;
mod atexit;
mod binascii;
mod bisect;
mod codecs;
mod collections;
mod csv;
@@ -104,6 +105,7 @@ pub fn get_module_inits() -> StdlibMap {
"array" => array::make_module,
"atexit" => atexit::make_module,
"binascii" => binascii::make_module,
"_bisect" => bisect::make_module,
"_codecs" => codecs::make_module,
"_collections" => collections::make_module,
"_csv" => csv::make_module,