Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pola-rs
GitHub Repository: pola-rs/polars
Path: blob/main/crates/polars-utils/src/slice_enum.rs
6939 views
1
use std::num::TryFromIntError;
2
use std::ops::Range;
3
4
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6
#[cfg_attr(feature = "dsl-schema", derive(schemars::JsonSchema))]
7
pub enum Slice {
8
/// Or zero
9
Positive {
10
offset: usize,
11
len: usize,
12
},
13
Negative {
14
offset_from_end: usize,
15
len: usize,
16
},
17
}
18
19
impl Slice {
20
#[allow(clippy::len_without_is_empty)]
21
pub fn len(&self) -> usize {
22
match self {
23
Slice::Positive { len, .. } => *len,
24
Slice::Negative { len, .. } => *len,
25
}
26
}
27
28
pub fn len_mut(&mut self) -> &mut usize {
29
match self {
30
Slice::Positive { len, .. } => len,
31
Slice::Negative { len, .. } => len,
32
}
33
}
34
35
/// Returns the offset of a positive slice.
36
///
37
/// # Panics
38
/// Panics if `self` is [`Slice::Negative`]
39
pub fn positive_offset(&self) -> usize {
40
let Slice::Positive { offset, len: _ } = self.clone() else {
41
panic!("cannot use positive_offset() on a negative slice");
42
};
43
44
offset
45
}
46
47
/// Returns the end position of the slice (offset + len).
48
///
49
/// # Panics
50
/// Panics if self is negative.
51
pub fn end_position(&self) -> usize {
52
let Slice::Positive { offset, len } = self.clone() else {
53
panic!("cannot use end_position() on a negative slice");
54
};
55
56
offset.saturating_add(len)
57
}
58
59
/// Returns the equivalent slice to apply from an offsetted position.
60
///
61
/// # Panics
62
/// Panics if self is negative.
63
pub fn offsetted(self, position: usize) -> Self {
64
let Slice::Positive { offset, len } = self else {
65
panic!("cannot use offsetted() on a negative slice");
66
};
67
68
let (offset, len) = if position <= offset {
69
(offset - position, len)
70
} else {
71
let n_past_offset = position - offset;
72
(0, len.saturating_sub(n_past_offset))
73
};
74
75
Slice::Positive { offset, len }
76
}
77
78
/// Restricts the bounds of the slice to within a number of rows. Negative slices will also
79
/// be translated to the positive equivalent.
80
pub fn restrict_to_bounds(self, n_rows: usize) -> Self {
81
match self {
82
Slice::Positive { offset, len } => {
83
let offset = offset.min(n_rows);
84
let len = len.min(n_rows - offset);
85
Slice::Positive { offset, len }
86
},
87
Slice::Negative {
88
offset_from_end,
89
len,
90
} => {
91
if n_rows >= offset_from_end {
92
// Trim extra starting rows
93
let offset = n_rows - offset_from_end;
94
let len = len.min(n_rows - offset);
95
Slice::Positive { offset, len }
96
} else {
97
// Slice offset goes past start of data.
98
let stop_at_n_from_end = offset_from_end.saturating_sub(len);
99
let len = n_rows.saturating_sub(stop_at_n_from_end);
100
101
Slice::Positive { offset: 0, len }
102
}
103
},
104
}
105
}
106
}
107
108
impl From<(usize, usize)> for Slice {
109
fn from((offset, len): (usize, usize)) -> Self {
110
Slice::Positive { offset, len }
111
}
112
}
113
114
impl From<(i64, usize)> for Slice {
115
fn from((offset, len): (i64, usize)) -> Self {
116
if offset >= 0 {
117
Slice::Positive {
118
offset: usize::try_from(offset).unwrap(),
119
len,
120
}
121
} else {
122
Slice::Negative {
123
offset_from_end: usize::try_from(-offset).unwrap(),
124
len,
125
}
126
}
127
}
128
}
129
130
impl TryFrom<Slice> for (i64, usize) {
131
type Error = TryFromIntError;
132
133
fn try_from(value: Slice) -> Result<Self, Self::Error> {
134
match value {
135
Slice::Positive { offset, len } => Ok((i64::try_from(offset)?, len)),
136
Slice::Negative {
137
offset_from_end,
138
len,
139
} => Ok((-i64::try_from(offset_from_end)?, len)),
140
}
141
}
142
}
143
144
impl From<Slice> for Range<usize> {
145
fn from(value: Slice) -> Self {
146
match value {
147
Slice::Positive { offset, len } => offset..offset.checked_add(len).unwrap(),
148
Slice::Negative { .. } => panic!("cannot convert negative slice into range"),
149
}
150
}
151
}
152
153
#[cfg(test)]
154
mod tests {
155
use super::Slice;
156
157
#[test]
158
fn test_slice_offset() {
159
assert_eq!(
160
Slice::Positive { offset: 3, len: 10 }.offsetted(1),
161
Slice::Positive { offset: 2, len: 10 }
162
);
163
assert_eq!(
164
Slice::Positive { offset: 3, len: 10 }.offsetted(5),
165
Slice::Positive { offset: 0, len: 8 }
166
);
167
}
168
169
#[test]
170
fn test_slice_restrict_to_bounds() {
171
assert_eq!(
172
Slice::Positive { offset: 3, len: 10 }.restrict_to_bounds(7),
173
Slice::Positive { offset: 3, len: 4 },
174
);
175
assert_eq!(
176
Slice::Positive { offset: 3, len: 10 }.restrict_to_bounds(0),
177
Slice::Positive { offset: 0, len: 0 },
178
);
179
assert_eq!(
180
Slice::Positive { offset: 3, len: 10 }.restrict_to_bounds(1),
181
Slice::Positive { offset: 1, len: 0 },
182
);
183
assert_eq!(
184
Slice::Positive { offset: 2, len: 0 }.restrict_to_bounds(10),
185
Slice::Positive { offset: 2, len: 0 },
186
);
187
assert_eq!(
188
Slice::Negative {
189
offset_from_end: 3,
190
len: 1
191
}
192
.restrict_to_bounds(4),
193
Slice::Positive { offset: 1, len: 1 },
194
);
195
assert_eq!(
196
Slice::Negative {
197
offset_from_end: 3,
198
len: 1
199
}
200
.restrict_to_bounds(1),
201
Slice::Positive { offset: 0, len: 0 },
202
);
203
}
204
}
205
206