mirror of
https://github.com/nushell/nushell.git
synced 2025-01-05 05:50:14 +01:00
Moved to using IntRange over SubBytes
This commit is contained in:
parent
d78142fe09
commit
a13c71e631
@ -1,15 +1,14 @@
|
||||
use nu_cmd_base::{
|
||||
input_handler::{operate, CmdArgument},
|
||||
util,
|
||||
};
|
||||
use std::ops::Bound;
|
||||
|
||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::Range;
|
||||
use nu_protocol::{IntRange, Range};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BytesAt;
|
||||
|
||||
struct Arguments {
|
||||
indexes: Subbytes,
|
||||
range: IntRange,
|
||||
cell_paths: Option<Vec<CellPath>>,
|
||||
}
|
||||
|
||||
@ -19,15 +18,6 @@ impl CmdArgument for Arguments {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(isize, isize)> for Subbytes {
|
||||
fn from(input: (isize, isize)) -> Self {
|
||||
Self(input.0, input.1)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Subbytes(isize, isize);
|
||||
|
||||
impl Command for BytesAt {
|
||||
fn name(&self) -> &str {
|
||||
"bytes at"
|
||||
@ -69,32 +59,32 @@ impl Command for BytesAt {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let range: Range = call.req(engine_state, stack, 0)?;
|
||||
let indexes: Subbytes = match util::process_range(&range) {
|
||||
Ok(idxs) => idxs.into(),
|
||||
Err(processing_error) => {
|
||||
return Err(processing_error("could not perform subbytes", call.head));
|
||||
let range = match call.req(engine_state, stack, 0)? {
|
||||
Range::IntRange(range) => range,
|
||||
_ => {
|
||||
return Err(ShellError::UnsupportedInput {
|
||||
msg: "Float ranges are not supported for byte streams".into(),
|
||||
input: "value originates from here".into(),
|
||||
msg_span: call.head,
|
||||
input_span: call.head,
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
let args = Arguments {
|
||||
indexes,
|
||||
cell_paths,
|
||||
};
|
||||
|
||||
if let PipelineData::ByteStream(stream, metadata) = input {
|
||||
let stream = stream.slice(
|
||||
call.head,
|
||||
call.arguments_span(),
|
||||
args.indexes.0,
|
||||
args.indexes.1,
|
||||
)?;
|
||||
|
||||
let stream = stream.slice(call.head, call.arguments_span(), range)?;
|
||||
Ok(PipelineData::ByteStream(stream, metadata))
|
||||
} else {
|
||||
operate(map_value, args, input, call.head, engine_state.signals())
|
||||
operate(
|
||||
map_value,
|
||||
Arguments { range, cell_paths },
|
||||
input,
|
||||
call.head,
|
||||
engine_state.signals(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,7 +99,7 @@ impl Command for BytesAt {
|
||||
},
|
||||
Example {
|
||||
description: "Slice out `0x[10 01 13]` from `0x[33 44 55 10 01 13]`",
|
||||
example: "0x[33 44 55 10 01 13] | bytes at 3..6",
|
||||
example: "0x[33 44 55 10 01 13] | bytes at 3..5",
|
||||
result: Some(Value::test_binary(vec![0x10, 0x01, 0x13])),
|
||||
},
|
||||
Example {
|
||||
@ -146,18 +136,15 @@ impl Command for BytesAt {
|
||||
}
|
||||
|
||||
fn map_value(input: &Value, args: &Arguments, head: Span) -> Value {
|
||||
let range = &args.indexes;
|
||||
let range = &args.range;
|
||||
match input {
|
||||
Value::Binary { val, .. } => {
|
||||
let (start, end) = resolve_relative_range(range, &val.len());
|
||||
let iter = val.iter().copied();
|
||||
|
||||
let bytes: Vec<u8> = if start > end {
|
||||
vec![]
|
||||
} else if end == usize::MAX {
|
||||
iter.skip(start).collect()
|
||||
} else {
|
||||
iter.skip(start).take(end - start + 1).collect()
|
||||
let len = val.len() as u64;
|
||||
let start: usize = range.absolute_start(len).try_into().unwrap();
|
||||
let bytes: Vec<u8> = match range.absolute_end(len) {
|
||||
Bound::Unbounded => val[start..].into(),
|
||||
Bound::Included(end) => val[start..=end as usize].into(),
|
||||
Bound::Excluded(end) => val[start..end as usize].into(),
|
||||
};
|
||||
|
||||
Value::binary(bytes, head)
|
||||
@ -175,20 +162,6 @@ fn map_value(input: &Value, args: &Arguments, head: Span) -> Value {
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_relative_range(range: &Subbytes, len: usize) -> (usize, usize) {
|
||||
let start = match range.0 {
|
||||
start if start < 0 => len.checked_sub(start.unsigned_abs()).unwrap_or(0),
|
||||
start => start as usize,
|
||||
};
|
||||
|
||||
let end = match range.1 {
|
||||
end if end < 0 => len.checked_sub(end.unsigned_abs()).unwrap_or(0),
|
||||
end => end as usize,
|
||||
};
|
||||
|
||||
(start, end)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
@ -1,8 +1,9 @@
|
||||
//! Module managing the streaming of raw bytes between pipeline elements
|
||||
#[cfg(feature = "os")]
|
||||
use crate::process::{ChildPipe, ChildProcess};
|
||||
use crate::{ErrSpan, IntoSpanned, PipelineData, ShellError, Signals, Span, Type, Value};
|
||||
use crate::{ErrSpan, IntRange, IntoSpanned, PipelineData, ShellError, Signals, Span, Type, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::ops::Bound;
|
||||
#[cfg(unix)]
|
||||
use std::os::fd::OwnedFd;
|
||||
#[cfg(windows)]
|
||||
@ -256,42 +257,46 @@ impl ByteStream {
|
||||
|
||||
pub fn slice(
|
||||
self,
|
||||
val_span: Span,
|
||||
range_span: Span,
|
||||
call_span: Span,
|
||||
start: isize,
|
||||
end: isize,
|
||||
range: IntRange,
|
||||
) -> Result<Self, ShellError> {
|
||||
match self.known_size {
|
||||
Some(len) => {
|
||||
let absolute_start = match start {
|
||||
start if start < 0 => (len as isize + start).max(0) as usize,
|
||||
start => start.min(len as isize) as usize,
|
||||
};
|
||||
|
||||
self.skip(val_span, absolute_start as u64)
|
||||
.and_then(|stream| {
|
||||
let absolute_end = match end {
|
||||
end if end < 0 => (len as isize + end).max(0) as usize,
|
||||
end => end.min(len as isize) as usize,
|
||||
};
|
||||
|
||||
if absolute_end < absolute_start {
|
||||
stream.take(val_span, 0)
|
||||
} else {
|
||||
stream.take(val_span, (absolute_end - absolute_start) as u64)
|
||||
}
|
||||
})
|
||||
let absolute_start = range.absolute_start(len);
|
||||
match range.absolute_end(len) {
|
||||
Bound::Unbounded => self.skip(range_span, absolute_start),
|
||||
Bound::Included(end) => self.skip(range_span, absolute_start)
|
||||
.and_then(|stream| stream.take(range_span, end.saturating_sub(absolute_start) + 1)),
|
||||
Bound::Excluded(end) => self.skip(range_span, absolute_start)
|
||||
.and_then(|stream| stream.take(range_span, end.saturating_sub(absolute_start))),
|
||||
}
|
||||
}
|
||||
None => match range.is_relative() {
|
||||
true => Err(ShellError::IncorrectValue {
|
||||
msg:
|
||||
"Relative range values cannot be used with streams that don't specify a length"
|
||||
.into(),
|
||||
val_span: range_span,
|
||||
call_span,
|
||||
}),
|
||||
false => {
|
||||
let skip = range.start();
|
||||
match range.end() {
|
||||
std::ops::Bound::Unbounded => self.skip(range_span, skip as u64),
|
||||
std::ops::Bound::Included(end) => match end - skip {
|
||||
take if take < 0 => self.take(range_span, 0),
|
||||
take => self.skip(range_span, skip as u64)
|
||||
.and_then(|stream| stream.take(range_span, take as u64 + 1)),
|
||||
},
|
||||
std::ops::Bound::Excluded(end) => match end - skip {
|
||||
take if take < 0 => self.take(range_span, 0),
|
||||
take => self.skip(range_span, skip as u64)
|
||||
.and_then(|stream| stream.take(range_span, take as u64)),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
None if start < 0 || end < 0 => Err(ShellError::IncorrectValue {
|
||||
msg:
|
||||
"Relative range values cannot be used with streams that don't specify a length"
|
||||
.into(),
|
||||
val_span,
|
||||
call_span,
|
||||
}),
|
||||
None => self
|
||||
.skip(val_span, start as u64)
|
||||
.and_then(|stream| stream.take(val_span, end as u64)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,10 +81,31 @@ mod int_range {
|
||||
self.start
|
||||
}
|
||||
|
||||
pub fn absolute_start(&self, len: u64) -> u64 {
|
||||
match self.start {
|
||||
start if start < 0 => len.saturating_sub(start.unsigned_abs().into()),
|
||||
start => start as u64,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end(&self) -> Bound<i64> {
|
||||
self.end
|
||||
}
|
||||
|
||||
pub fn absolute_end(&self, len: u64) -> Bound<u64> {
|
||||
match self.end {
|
||||
Bound::Unbounded => Bound::Unbounded,
|
||||
Bound::Included(i) => Bound::Included(match i {
|
||||
i if i < 0 => len.saturating_sub(i.unsigned_abs() as u64),
|
||||
i => i as u64,
|
||||
}),
|
||||
Bound::Excluded(i) => Bound::Excluded(match i {
|
||||
i if i < 0 => len.saturating_sub(i.unsigned_abs() as u64),
|
||||
i => i as u64,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn step(&self) -> i64 {
|
||||
self.step
|
||||
}
|
||||
@ -93,6 +114,21 @@ mod int_range {
|
||||
self.end == Bound::Unbounded
|
||||
}
|
||||
|
||||
pub fn is_relative(&self) -> bool {
|
||||
self.is_start_relative() || self.is_end_relative()
|
||||
}
|
||||
|
||||
pub fn is_start_relative(&self) -> bool {
|
||||
self.start < 0
|
||||
}
|
||||
|
||||
pub fn is_end_relative(&self) -> bool {
|
||||
match self.end {
|
||||
Bound::Included(end) | Bound::Excluded(end) => end < 0,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains(&self, value: i64) -> bool {
|
||||
if self.step < 0 {
|
||||
// Decreasing range
|
||||
|
@ -1,78 +1,252 @@
|
||||
use nu_protocol::{ByteStream, Signals, Span};
|
||||
use nu_protocol::{ast::RangeInclusion, ByteStream, IntRange, Signals, Span, Value};
|
||||
|
||||
#[test]
|
||||
pub fn test_simple_positive_slice() {
|
||||
pub fn test_simple_positive_slice_exclusive() {
|
||||
let data = b"Hello World".to_vec();
|
||||
let stream = ByteStream::read_binary(data, Span::test_data(), Signals::empty());
|
||||
let sliced = stream
|
||||
.slice(Span::test_data(), Span::test_data(), 0, 5)
|
||||
.slice(
|
||||
Span::test_data(),
|
||||
Span::test_data(),
|
||||
create_range(0, 5, RangeInclusion::RightExclusive),
|
||||
)
|
||||
.unwrap();
|
||||
let result = sliced.into_bytes().unwrap();
|
||||
assert_eq!(result, b"Hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_negative_start() {
|
||||
pub fn test_negative_start_exclusive() {
|
||||
let data = b"Hello World".to_vec();
|
||||
let stream = ByteStream::read_binary(data, Span::test_data(), Signals::empty());
|
||||
let sliced = stream
|
||||
.slice(Span::test_data(), Span::test_data(), -5, 11)
|
||||
.slice(
|
||||
Span::test_data(),
|
||||
Span::test_data(),
|
||||
create_range(-5, 11, RangeInclusion::RightExclusive),
|
||||
)
|
||||
.unwrap();
|
||||
let result = sliced.into_bytes().unwrap();
|
||||
assert_eq!(result, b"World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_negative_end() {
|
||||
pub fn test_negative_end_exclusive() {
|
||||
let data = b"Hello World".to_vec();
|
||||
let stream = ByteStream::read_binary(data, Span::test_data(), Signals::empty());
|
||||
let sliced = stream
|
||||
.slice(Span::test_data(), Span::test_data(), 0, -6)
|
||||
.slice(
|
||||
Span::test_data(),
|
||||
Span::test_data(),
|
||||
create_range(0, -6, RangeInclusion::RightExclusive),
|
||||
)
|
||||
.unwrap();
|
||||
let result = sliced.into_bytes().unwrap();
|
||||
assert_eq!(result, b"Hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_empty_slice() {
|
||||
pub fn test_negative_start_and_end_exclusive() {
|
||||
let data = b"Hello World".to_vec();
|
||||
let stream = ByteStream::read_binary(data, Span::test_data(), Signals::empty());
|
||||
let sliced = stream
|
||||
.slice(Span::test_data(), Span::test_data(), 5, 5)
|
||||
.slice(
|
||||
Span::test_data(),
|
||||
Span::test_data(),
|
||||
create_range(-5, -2, RangeInclusion::RightExclusive),
|
||||
)
|
||||
.unwrap();
|
||||
let result = sliced.into_bytes().unwrap();
|
||||
assert_eq!(result, b"Wor");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_empty_slice_exclusive() {
|
||||
let data = b"Hello World".to_vec();
|
||||
let stream = ByteStream::read_binary(data, Span::test_data(), Signals::empty());
|
||||
let sliced = stream
|
||||
.slice(
|
||||
Span::test_data(),
|
||||
Span::test_data(),
|
||||
create_range(5, 5, RangeInclusion::RightExclusive),
|
||||
)
|
||||
.unwrap();
|
||||
let result = sliced.into_bytes().unwrap();
|
||||
assert_eq!(result, Vec::<u8>::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_out_of_bounds() {
|
||||
pub fn test_out_of_bounds_exclusive() {
|
||||
let data = b"Hello World".to_vec();
|
||||
let stream = ByteStream::read_binary(data, Span::test_data(), Signals::empty());
|
||||
let sliced = stream
|
||||
.slice(Span::test_data(), Span::test_data(), 0, 20)
|
||||
.slice(
|
||||
Span::test_data(),
|
||||
Span::test_data(),
|
||||
create_range(0, 20, RangeInclusion::RightExclusive),
|
||||
)
|
||||
.unwrap();
|
||||
let result = sliced.into_bytes().unwrap();
|
||||
assert_eq!(result, b"Hello World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_invalid_range() {
|
||||
pub fn test_invalid_range_exclusive() {
|
||||
let data = b"Hello World".to_vec();
|
||||
let stream = ByteStream::read_binary(data, Span::test_data(), Signals::empty());
|
||||
let sliced = stream
|
||||
.slice(Span::test_data(), Span::test_data(), 11, 5)
|
||||
.slice(
|
||||
Span::test_data(),
|
||||
Span::test_data(),
|
||||
create_range(11, 5, RangeInclusion::RightExclusive),
|
||||
)
|
||||
.unwrap();
|
||||
let result = sliced.into_bytes().unwrap();
|
||||
assert_eq!(result, Vec::<u8>::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_max_end() {
|
||||
pub fn test_max_end_exclusive() {
|
||||
let data = b"Hello World".to_vec();
|
||||
let stream = ByteStream::read_binary(data, Span::test_data(), Signals::empty());
|
||||
let sliced = stream
|
||||
.slice(Span::test_data(), Span::test_data(), 6, isize::MAX)
|
||||
.slice(
|
||||
Span::test_data(),
|
||||
Span::test_data(),
|
||||
create_range(6, i64::MAX, RangeInclusion::RightExclusive),
|
||||
)
|
||||
.unwrap();
|
||||
let result = sliced.into_bytes().unwrap();
|
||||
assert_eq!(result, b"World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_simple_positive_slice_inclusive() {
|
||||
let data = b"Hello World".to_vec();
|
||||
let stream = ByteStream::read_binary(data, Span::test_data(), Signals::empty());
|
||||
let sliced = stream
|
||||
.slice(
|
||||
Span::test_data(),
|
||||
Span::test_data(),
|
||||
create_range(0, 5, RangeInclusion::RightExclusive),
|
||||
)
|
||||
.unwrap();
|
||||
let result = sliced.into_bytes().unwrap();
|
||||
assert_eq!(result, b"Hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_negative_start_inclusive() {
|
||||
let data = b"Hello World".to_vec();
|
||||
let stream = ByteStream::read_binary(data, Span::test_data(), Signals::empty());
|
||||
let sliced = stream
|
||||
.slice(
|
||||
Span::test_data(),
|
||||
Span::test_data(),
|
||||
create_range(-5, 11, RangeInclusion::Inclusive),
|
||||
)
|
||||
.unwrap();
|
||||
let result = sliced.into_bytes().unwrap();
|
||||
assert_eq!(result, b"World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_negative_end_inclusive() {
|
||||
let data = b"Hello World".to_vec();
|
||||
let stream = ByteStream::read_binary(data, Span::test_data(), Signals::empty());
|
||||
let sliced = stream
|
||||
.slice(
|
||||
Span::test_data(),
|
||||
Span::test_data(),
|
||||
create_range(0, -7, RangeInclusion::Inclusive),
|
||||
)
|
||||
.unwrap();
|
||||
let result = sliced.into_bytes().unwrap();
|
||||
assert_eq!(result, b"Hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_negative_start_and_end_inclusive() {
|
||||
let data = b"Hello World".to_vec();
|
||||
let stream = ByteStream::read_binary(data, Span::test_data(), Signals::empty());
|
||||
let sliced = stream
|
||||
.slice(
|
||||
Span::test_data(),
|
||||
Span::test_data(),
|
||||
create_range(-5, -1, RangeInclusion::Inclusive),
|
||||
)
|
||||
.unwrap();
|
||||
let result = sliced.into_bytes().unwrap();
|
||||
assert_eq!(result, b"World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_empty_slice_inclusive() {
|
||||
let data = b"Hello World".to_vec();
|
||||
let stream = ByteStream::read_binary(data, Span::test_data(), Signals::empty());
|
||||
let sliced = stream
|
||||
.slice(
|
||||
Span::test_data(),
|
||||
Span::test_data(),
|
||||
create_range(5, 5, RangeInclusion::Inclusive),
|
||||
)
|
||||
.unwrap();
|
||||
let result = sliced.into_bytes().unwrap();
|
||||
assert_eq!(result, b" ");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_out_of_bounds_inclusive() {
|
||||
let data = b"Hello World".to_vec();
|
||||
let stream = ByteStream::read_binary(data, Span::test_data(), Signals::empty());
|
||||
let sliced = stream
|
||||
.slice(
|
||||
Span::test_data(),
|
||||
Span::test_data(),
|
||||
create_range(0, 20, RangeInclusion::Inclusive),
|
||||
)
|
||||
.unwrap();
|
||||
let result = sliced.into_bytes().unwrap();
|
||||
assert_eq!(result, b"Hello World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_invalid_range_inclusive() {
|
||||
let data = b"Hello World".to_vec();
|
||||
let stream = ByteStream::read_binary(data, Span::test_data(), Signals::empty());
|
||||
let sliced = stream
|
||||
.slice(
|
||||
Span::test_data(),
|
||||
Span::test_data(),
|
||||
create_range(11, 5, RangeInclusion::Inclusive),
|
||||
)
|
||||
.unwrap();
|
||||
let result = sliced.into_bytes().unwrap();
|
||||
assert_eq!(result, Vec::<u8>::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_max_end_inclusive() {
|
||||
let data = b"Hello World".to_vec();
|
||||
let stream = ByteStream::read_binary(data, Span::test_data(), Signals::empty());
|
||||
let sliced = stream
|
||||
.slice(
|
||||
Span::test_data(),
|
||||
Span::test_data(),
|
||||
create_range(6, i64::MAX, RangeInclusion::Inclusive),
|
||||
)
|
||||
.unwrap();
|
||||
let result = sliced.into_bytes().unwrap();
|
||||
assert_eq!(result, b"World");
|
||||
}
|
||||
|
||||
fn create_range(start: i64, end: i64, inclusion: RangeInclusion) -> IntRange {
|
||||
IntRange::new(
|
||||
Value::int(start, Span::unknown()),
|
||||
Value::nothing(Span::test_data()),
|
||||
Value::int(end, Span::unknown()),
|
||||
inclusion,
|
||||
Span::unknown(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user