-
Notifications
You must be signed in to change notification settings - Fork 2
Array slices #22
Array slices #22
Changes from 3 commits
3ee433e
82a630f
4099d13
81d4d2c
df0a708
43f7113
e0022a6
bfe83b7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,8 @@ | |
*/ | ||
|
||
use serde_json::Value; | ||
use std::cmp::Ordering; | ||
use std::iter; | ||
|
||
/// A path is a tree of selector nodes. | ||
/// | ||
|
@@ -57,9 +59,17 @@ pub enum Selector { | |
#[derive(Debug)] | ||
pub enum UnionElement { | ||
Name(String), | ||
Slice(Slice), | ||
Index(i64), | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct Slice { | ||
pub start: Option<isize>, | ||
pub end: Option<isize>, | ||
pub step: Option<isize>, | ||
} | ||
|
||
type Iter<'a> = Box<dyn Iterator<Item = &'a Value> + 'a>; | ||
|
||
impl Path { | ||
|
@@ -89,11 +99,69 @@ impl UnionElement { | |
pub fn get<'a>(&self, v: &'a Value) -> Iter<'a> { | ||
match self { | ||
UnionElement::Name(name) => Box::new(v.get(name).into_iter()), | ||
UnionElement::Slice(slice) => { | ||
if let Value::Array(arr) = v { | ||
let step = slice.step.unwrap_or(1); | ||
|
||
let len = arr.len(); | ||
|
||
let start = slice | ||
.start | ||
.map(|s| if s < 0 { s + (len as isize) } else { s }) | ||
.unwrap_or(if step > 0 { 0 } else { (len as isize) - 1 }); | ||
|
||
let end = slice | ||
.end | ||
.map(|e| if e < 0 { e + (len as isize) } else { e }) | ||
.unwrap_or(if step > 0 { len as isize } else { -1 }); | ||
|
||
Box::new(array_slice(arr, start, end, step)) | ||
} else { | ||
Box::new(iter::empty()) | ||
} | ||
} | ||
UnionElement::Index(num) => Box::new(v.get(abs_index(*num, v)).into_iter()), | ||
} | ||
} | ||
} | ||
|
||
fn array_slice(arr: &[Value], start: isize, end: isize, step: isize) -> Iter<'_> { | ||
let len = arr.len(); | ||
let mut sl = vec![]; | ||
match step.cmp(&0) { | ||
Ordering::Greater => { | ||
let strt = if start < 0 { 0 } else { start as usize }; // avoid CPU attack | ||
let e = if end > (len as isize) { | ||
len | ||
} else { | ||
end as usize | ||
}; // avoid CPU attack | ||
for i in (strt..e).step_by(step as usize) { | ||
if i < len { | ||
sl.push(&arr[i]); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The spec says:
then it describes the iterations as:
what should happen if you call this on an input slice of length 5?: let s = array_slice(j.as_array().unwrap(), -5, 0, 1); My interpretation is that it would yield
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a good example of a reference implementation and compliance test suite turning up a hole in the spec. I think the spec should say (only more formally/precisely) that negative So, for an array of length 5,
of which there are none, so the result is empty. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Desugaring before iterating makes most sense to me as well. I was tinkering with the consequences if doing it the other way around and I don't think it would provide any advantage. An interesting consequence is that it allows the resulting slice to be longer than the input array (and contain repeated elements) which I think would be quite confusing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the implementation is correct and we just need to fix the spec. ;-) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the privileges of a spec writer! :-) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if something like this would have less special casing: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=dd7180d6586e5a511b4e56b0058e5562 While I don't think it's fully correct w.r.t overflows etc, but has a few advantages:
|
||
} | ||
} | ||
} | ||
|
||
Ordering::Less => { | ||
let strt = if start > (len as isize) { | ||
len as isize | ||
} else { | ||
start | ||
}; // avoid CPU attack | ||
let e = if end < -1 { -1 } else { end }; // avoid CPU attack | ||
for i in (-strt..-e).step_by(-step as usize) { | ||
if 0 <= -i && -i < (len as isize) { | ||
sl.push(&arr[-i as usize]); | ||
} | ||
} | ||
} | ||
|
||
Ordering::Equal => (), | ||
} | ||
Box::new(sl.into_iter()) | ||
} | ||
|
||
fn abs_index(index: i64, node: &Value) -> usize { | ||
if index >= 0 { | ||
index as usize | ||
|
Uh oh!
There was an error while loading. Please reload this page.