Skip to content

Commit a30a170

Browse files
authored
Merge pull request RustPython#3810 from youknowone/sequence-index-op
SequenceIndexOp for saturate_index and wrap_index
2 parents b8f276f + b0b5fac commit a30a170

File tree

8 files changed

+77
-69
lines changed

8 files changed

+77
-69
lines changed

stdlib/src/array.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ mod array {
2525
},
2626
sequence::{SequenceExt, SequenceMutExt},
2727
sliceable::{
28-
SaturatedSlice, SequenceIndex, SliceableSequenceMutOp, SliceableSequenceOp,
28+
SaturatedSlice, SequenceIndex, SequenceIndexOp, SliceableSequenceMutOp,
29+
SliceableSequenceOp,
2930
},
3031
types::{
3132
AsBuffer, AsMapping, Comparable, Constructor, IterNext, IterNextIterable, Iterable,
@@ -128,7 +129,7 @@ mod array {
128129
match self {
129130
$(ArrayContentType::$n(v) => {
130131
let val = <$t>::try_into_from_object(vm, obj)?;
131-
v.insert(v.saturate_index(i), val);
132+
v.insert(i.saturated_at(v.len()), val);
132133
})*
133134
}
134135
Ok(())

stdlib/src/mmap.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ mod mmap {
1414
protocol::{
1515
BufferDescriptor, BufferMethods, PyBuffer, PyMappingMethods, PySequenceMethods,
1616
},
17-
sliceable::{saturate_index, wrap_index, SaturatedSlice, SequenceIndex},
17+
sliceable::{SaturatedSlice, SequenceIndex, SequenceIndexOp},
1818
types::{AsBuffer, AsMapping, AsSequence, Constructor},
1919
AsObject, FromArgs, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
2020
TryFromBorrowedObject, VirtualMachine,
@@ -592,11 +592,11 @@ mod mmap {
592592
let size = self.len();
593593
let start = options
594594
.start
595-
.map(|start| saturate_index(start, size))
595+
.map(|start| start.saturated_at(size))
596596
.unwrap_or_else(|| self.pos());
597597
let end = options
598598
.end
599-
.map(|end| saturate_index(end, size))
599+
.map(|end| end.saturated_at(size))
600600
.unwrap_or(size);
601601
(start, end)
602602
}
@@ -918,7 +918,8 @@ mod mmap {
918918
}
919919

920920
fn getitem_by_index(&self, i: isize, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
921-
let i = wrap_index(i, self.len())
921+
let i = i
922+
.wrapped_at(self.len())
922923
.ok_or_else(|| vm.new_index_error("mmap index out of range".to_owned()))?;
923924

924925
let b = match self.check_valid(vm)?.deref().as_ref().unwrap() {
@@ -999,7 +1000,8 @@ mod mmap {
9991000
value: PyObjectRef,
10001001
vm: &VirtualMachine,
10011002
) -> PyResult<()> {
1002-
let i = wrap_index(i, zelf.len())
1003+
let i = i
1004+
.wrapped_at(zelf.len())
10031005
.ok_or_else(|| vm.new_index_error("mmap index out of range".to_owned()))?;
10041006

10051007
let b = value_from_object(vm, &value)?;

vm/src/builtins/list.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::{
1010
protocol::{PyIterReturn, PyMappingMethods, PySequence, PySequenceMethods},
1111
recursion::ReprGuard,
1212
sequence::{MutObjectSequenceOp, SequenceExt, SequenceMutExt},
13-
sliceable::{pyint_saturate_index, SequenceIndex, SliceableSequenceMutOp, SliceableSequenceOp},
13+
sliceable::{SequenceIndex, SequenceIndexOp, SliceableSequenceMutOp, SliceableSequenceOp},
1414
types::{
1515
AsMapping, AsSequence, Comparable, Constructor, Hashable, Initializer, IterNext,
1616
IterNextIterable, Iterable, PyComparisonOp, Unconstructible, Unhashable,
@@ -276,7 +276,7 @@ impl PyList {
276276
let len = self.len();
277277
let saturate = |obj: PyObjectRef, len| -> PyResult<_> {
278278
obj.try_into_value(vm)
279-
.map(|int: PyIntRef| pyint_saturate_index(int, len))
279+
.map(|int: PyIntRef| int.as_bigint().saturated_at(len))
280280
};
281281
let start = start.map_or(Ok(0), |obj| saturate(obj, len))?;
282282
let stop = stop.map_or(Ok(len), |obj| saturate(obj, len))?;

vm/src/builtins/memory.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::{
1717
protocol::{
1818
BufferDescriptor, BufferMethods, PyBuffer, PyMappingMethods, PySequenceMethods, VecBuffer,
1919
},
20-
sliceable::wrap_index,
20+
sliceable::SequenceIndexOp,
2121
types::{AsBuffer, AsMapping, AsSequence, Comparable, Constructor, Hashable, PyComparisonOp},
2222
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
2323
TryFromBorrowedObject, TryFromObject, VirtualMachine,
@@ -243,7 +243,8 @@ impl PyMemoryView {
243243
));
244244
}
245245
let (shape, stride, suboffset) = self.desc.dim_desc[0];
246-
let index = wrap_index(i, shape)
246+
let index = i
247+
.wrapped_at(shape)
247248
.ok_or_else(|| vm.new_index_error("index out of range".to_owned()))?;
248249
let index = index as isize * stride + suboffset;
249250
let pos = (index + self.start as isize) as usize;
@@ -292,7 +293,8 @@ impl PyMemoryView {
292293
return Err(vm.new_not_implemented_error("sub-views are not implemented".to_owned()));
293294
}
294295
let (shape, stride, suboffset) = self.desc.dim_desc[0];
295-
let index = wrap_index(i, shape)
296+
let index = i
297+
.wrapped_at(shape)
296298
.ok_or_else(|| vm.new_index_error("index out of range".to_owned()))?;
297299
let index = index as isize * stride + suboffset;
298300
let pos = (index + self.start as isize) as usize;
@@ -316,7 +318,7 @@ impl PyMemoryView {
316318
if zelf.is(&src) {
317319
return if !is_equiv_structure(&zelf.desc, &dest.desc) {
318320
Err(vm.new_value_error(
319-
"memoryview assigment: lvalue and rvalue have different structures".to_owned(),
321+
"memoryview assignment: lvalue and rvalue have different structures".to_owned(),
320322
))
321323
} else {
322324
// assign self[:] to self
@@ -336,7 +338,7 @@ impl PyMemoryView {
336338

337339
if !is_equiv_structure(&src.desc, &dest.desc) {
338340
return Err(vm.new_value_error(
339-
"memoryview assigment: lvalue and rvalue have different structures".to_owned(),
341+
"memoryview assignment: lvalue and rvalue have different structures".to_owned(),
340342
));
341343
}
342344

vm/src/builtins/tuple.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{
88
protocol::{PyIterReturn, PyMappingMethods, PySequenceMethods},
99
recursion::ReprGuard,
1010
sequence::SequenceExt,
11-
sliceable::pyint_saturate_index,
11+
sliceable::SequenceIndexOp,
1212
sliceable::{SequenceIndex, SliceableSequenceOp},
1313
types::{
1414
AsMapping, AsSequence, Comparable, Constructor, Hashable, IterNext, IterNextIterable,
@@ -288,7 +288,7 @@ impl PyTuple {
288288
let len = self.len();
289289
let saturate = |obj: PyObjectRef, len| -> PyResult<_> {
290290
obj.try_into_value(vm)
291-
.map(|int: PyIntRef| pyint_saturate_index(int, len))
291+
.map(|int: PyIntRef| int.as_bigint().saturated_at(len))
292292
};
293293
let start = start.map_or(Ok(0), |i| saturate(i, len))?;
294294
let stop = stop.map_or(Ok(len), |i| saturate(i, len))?;

vm/src/protocol/buffer.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{
77
lock::{MapImmutable, PyMutex, PyMutexGuard},
88
},
99
object::PyObjectPayload,
10-
sliceable::wrap_index,
10+
sliceable::SequenceIndexOp,
1111
types::{Constructor, Unconstructible},
1212
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromBorrowedObject,
1313
VirtualMachine,
@@ -255,7 +255,7 @@ impl BufferDescriptor {
255255
.cloned()
256256
.zip_eq(self.dim_desc.iter().cloned())
257257
{
258-
let i = wrap_index(i, shape).ok_or_else(|| {
258+
let i = i.wrapped_at(shape).ok_or_else(|| {
259259
vm.new_index_error(format!("index out of bounds on dimension {}", i))
260260
})?;
261261
pos += i as isize * stride + suboffset;

vm/src/sliceable.rs

Lines changed: 49 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
// export through slicable module, not slice.
1+
// export through sliceable module, not slice.
22
use crate::{
3-
builtins::{int::PyInt, slice::PySlice, PyIntRef},
3+
builtins::{int::PyInt, slice::PySlice},
44
AsObject, PyObject, PyResult, VirtualMachine,
55
};
6+
use num_bigint::BigInt;
67
use num_traits::{Signed, ToPrimitive};
78
use std::ops::Range;
89

@@ -12,7 +13,7 @@ where
1213
{
1314
type Item: Clone;
1415
fn do_set(&mut self, index: usize, value: Self::Item);
15-
fn do_delele(&mut self, index: usize);
16+
fn do_delete(&mut self, index: usize);
1617
/// as CPython, length of range and items could be different, function must act like Vec::splice()
1718
fn do_set_range(&mut self, range: Range<usize>, items: &[Self::Item]);
1819
fn do_delete_range(&mut self, range: Range<usize>);
@@ -33,7 +34,7 @@ where
3334
let pos = self
3435
.as_ref()
3536
.wrap_index(index)
36-
.ok_or_else(|| vm.new_index_error("assigment index out of range".to_owned()))?;
37+
.ok_or_else(|| vm.new_index_error("assignment index out of range".to_owned()))?;
3738
self.do_set(pos, value);
3839
Ok(())
3940
}
@@ -89,8 +90,8 @@ where
8990
let pos = self
9091
.as_ref()
9192
.wrap_index(index)
92-
.ok_or_else(|| vm.new_index_error("assigment index out of range".to_owned()))?;
93-
self.do_delele(pos);
93+
.ok_or_else(|| vm.new_index_error("assignment index out of range".to_owned()))?;
94+
self.do_delete(pos);
9495
Ok(())
9596
}
9697

@@ -118,7 +119,7 @@ impl<T: Clone> SliceableSequenceMutOp for Vec<T> {
118119
self[index] = value;
119120
}
120121

121-
fn do_delele(&mut self, index: usize) {
122+
fn do_delete(&mut self, index: usize) {
122123
self.remove(index);
123124
}
124125

@@ -175,11 +176,11 @@ pub trait SliceableSequenceOp {
175176
fn len(&self) -> usize;
176177

177178
fn wrap_index(&self, p: isize) -> Option<usize> {
178-
wrap_index(p, self.len())
179+
p.wrapped_at(self.len())
179180
}
180181

181182
fn saturate_index(&self, p: isize) -> usize {
182-
saturate_index(p, self.len())
183+
p.saturated_at(self.len())
183184
}
184185

185186
fn getitem_by_slice(
@@ -275,7 +276,7 @@ impl SequenceIndex {
275276
} else if let Some(slice) = obj.payload::<PySlice>() {
276277
slice.to_saturated(vm).map(Self::Slice)
277278
} else if let Some(i) = vm.to_index_opt(obj.to_owned()) {
278-
// TODO: __index__ for indice is no more supported?
279+
// TODO: __index__ for indices is no more supported?
279280
i?.try_to_primitive(vm)
280281
.map_err(|_| {
281282
vm.new_index_error("cannot fit 'int' into an index-sized integer".to_owned())
@@ -291,50 +292,53 @@ impl SequenceIndex {
291292
}
292293
}
293294

294-
// Use PySliceableSequence::wrap_index for implementors
295-
pub fn wrap_index(p: isize, len: usize) -> Option<usize> {
296-
let neg = p.is_negative();
297-
let p = p.wrapping_abs() as usize;
298-
if neg {
299-
len.checked_sub(p)
300-
} else if p >= len {
301-
None
302-
} else {
303-
Some(p)
304-
}
295+
pub trait SequenceIndexOp {
296+
// Saturate p in range [0, len] inclusive
297+
fn saturated_at(&self, len: usize) -> usize;
298+
// Use PySliceableSequence::wrap_index for implementors
299+
fn wrapped_at(&self, len: usize) -> Option<usize>;
305300
}
306301

307-
// Saturate p in range [0, len] inclusive
308-
pub fn saturate_index(p: isize, len: usize) -> usize {
309-
let len = len.to_isize().unwrap_or(isize::MAX);
310-
let mut p = p;
311-
if p < 0 {
312-
p += len;
302+
impl SequenceIndexOp for isize {
303+
fn saturated_at(&self, len: usize) -> usize {
304+
let len = len.to_isize().unwrap_or(Self::MAX);
305+
let mut p = *self;
313306
if p < 0 {
314-
p = 0;
307+
p += len;
315308
}
309+
p.clamp(0, len) as usize
316310
}
317-
if p > len {
318-
p = len;
311+
312+
fn wrapped_at(&self, len: usize) -> Option<usize> {
313+
let neg = self.is_negative();
314+
let p = self.unsigned_abs();
315+
if neg {
316+
len.checked_sub(p)
317+
} else if p >= len {
318+
None
319+
} else {
320+
Some(p)
321+
}
319322
}
320-
p as usize
321323
}
322324

323-
// Saturate p in range [0, len] inclusive
324-
pub fn pyint_saturate_index(p: PyIntRef, len: usize) -> usize {
325-
let bigint = p.as_bigint();
326-
if bigint.is_negative() {
327-
bigint
328-
.abs()
329-
.try_into()
330-
.map_or(0, |abs| len.saturating_sub(abs))
331-
} else {
332-
bigint.try_into().unwrap_or(len)
325+
impl SequenceIndexOp for BigInt {
326+
fn saturated_at(&self, len: usize) -> usize {
327+
if self.is_negative() {
328+
self.abs()
329+
.try_into()
330+
.map_or(0, |abs| len.saturating_sub(abs))
331+
} else {
332+
self.try_into().unwrap_or(len)
333+
}
334+
}
335+
fn wrapped_at(&self, _len: usize) -> Option<usize> {
336+
unimplemented!("please add one once we need it")
333337
}
334338
}
335339

336340
/// A saturated slice with values ranging in [isize::MIN, isize::MAX]. Used for
337-
/// slicable sequences that require indices in the aforementioned range.
341+
/// sliceable sequences that require indices in the aforementioned range.
338342
///
339343
/// Invokes `__index__` on the PySliceRef during construction so as to separate the
340344
/// transformation from PyObject into isize and the adjusting of the slice to a given
@@ -383,16 +387,16 @@ impl SaturatedSlice {
383387
let stop = if self.stop == -1 {
384388
len
385389
} else {
386-
saturate_index(self.stop.saturating_add(1), len)
390+
self.stop.saturating_add(1).saturated_at(len)
387391
};
388392
let start = if self.start == -1 {
389393
len
390394
} else {
391-
saturate_index(self.start.saturating_add(1), len)
395+
self.start.saturating_add(1).saturated_at(len)
392396
};
393397
stop..start
394398
} else {
395-
saturate_index(self.start, len)..saturate_index(self.stop, len)
399+
self.start.saturated_at(len)..self.stop.saturated_at(len)
396400
};
397401

398402
let (range, slice_len) = if range.start >= range.end {

vm/src/stdlib/collections.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ mod _collections {
1313
protocol::{PyIterReturn, PySequenceMethods},
1414
recursion::ReprGuard,
1515
sequence::MutObjectSequenceOp,
16-
sliceable,
17-
sliceable::pyint_saturate_index,
16+
sliceable::SequenceIndexOp,
1817
types::{
1918
AsSequence, Comparable, Constructor, Hashable, Initializer, IterNext, IterNextIterable,
2019
Iterable, PyComparisonOp, Unhashable,
@@ -167,7 +166,7 @@ mod _collections {
167166
let len = self.len();
168167
let saturate = |obj: PyObjectRef, len| -> PyResult<_> {
169168
obj.try_into_value(vm)
170-
.map(|int: PyIntRef| pyint_saturate_index(int, len))
169+
.map(|int: PyIntRef| int.as_bigint().saturated_at(len))
171170
};
172171
let start = start.map_or(Ok(0), |i| saturate(i, len))?;
173172
let stop = stop.map_or(Ok(len), |i| saturate(i, len))?;
@@ -280,15 +279,15 @@ mod _collections {
280279
#[pymethod(magic)]
281280
fn getitem(&self, idx: isize, vm: &VirtualMachine) -> PyResult {
282281
let deque = self.borrow_deque();
283-
sliceable::wrap_index(idx, deque.len())
282+
idx.wrapped_at(deque.len())
284283
.and_then(|i| deque.get(i).cloned())
285284
.ok_or_else(|| vm.new_index_error("deque index out of range".to_owned()))
286285
}
287286

288287
#[pymethod(magic)]
289288
fn setitem(&self, idx: isize, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
290289
let mut deque = self.borrow_deque_mut();
291-
sliceable::wrap_index(idx, deque.len())
290+
idx.wrapped_at(deque.len())
292291
.and_then(|i| deque.get_mut(i))
293292
.map(|x| *x = value)
294293
.ok_or_else(|| vm.new_index_error("deque index out of range".to_owned()))
@@ -297,7 +296,7 @@ mod _collections {
297296
#[pymethod(magic)]
298297
fn delitem(&self, idx: isize, vm: &VirtualMachine) -> PyResult<()> {
299298
let mut deque = self.borrow_deque_mut();
300-
sliceable::wrap_index(idx, deque.len())
299+
idx.wrapped_at(deque.len())
301300
.and_then(|i| deque.remove(i).map(drop))
302301
.ok_or_else(|| vm.new_index_error("deque index out of range".to_owned()))
303302
}

0 commit comments

Comments
 (0)