1use super::{InputNumType, NumberBytes, get_input_num_type, get_number_bytes};
2use nu_cmd_base::input_handler::{CmdArgument, operate};
3use nu_engine::command_prelude::*;
4
5struct Arguments {
6 signed: bool,
7 bits: Spanned<usize>,
8 number_size: NumberBytes,
9}
10
11impl CmdArgument for Arguments {
12 fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
13 None
14 }
15}
16
17#[derive(Clone)]
18pub struct BitsRor;
19
20impl Command for BitsRor {
21 fn name(&self) -> &str {
22 "bits ror"
23 }
24
25 fn signature(&self) -> Signature {
26 Signature::build("bits ror")
27 .input_output_types(vec![
28 (Type::Int, Type::Int),
29 (Type::Binary, Type::Binary),
30 (
31 Type::List(Box::new(Type::Int)),
32 Type::List(Box::new(Type::Int)),
33 ),
34 (
35 Type::List(Box::new(Type::Binary)),
36 Type::List(Box::new(Type::Binary)),
37 ),
38 ])
39 .allow_variants_without_examples(true)
40 .required("bits", SyntaxShape::Int, "Number of bits to rotate right.")
41 .switch(
42 "signed",
43 "always treat input number as a signed number",
44 Some('s'),
45 )
46 .named(
47 "number-bytes",
48 SyntaxShape::Int,
49 "the word size in number of bytes. Must be `1`, `2`, `4`, or `8` (defaults to the smallest of those that fits the input number)",
50 Some('n'),
51 )
52 .category(Category::Bits)
53 }
54
55 fn description(&self) -> &str {
56 "Bitwise rotate right for ints or binary values."
57 }
58
59 fn search_terms(&self) -> Vec<&str> {
60 vec!["rotate right"]
61 }
62
63 fn run(
64 &self,
65 engine_state: &EngineState,
66 stack: &mut Stack,
67 call: &Call,
68 input: PipelineData,
69 ) -> Result<PipelineData, ShellError> {
70 let head = call.head;
71 let bits = call.req(engine_state, stack, 0)?;
72 let signed = call.has_flag(engine_state, stack, "signed")?;
73 let number_bytes: Option<Spanned<usize>> =
74 call.get_flag(engine_state, stack, "number-bytes")?;
75 let number_size = get_number_bytes(number_bytes, head)?;
76
77 if let PipelineData::Empty = input {
79 return Err(ShellError::PipelineEmpty { dst_span: head });
80 }
81
82 let args = Arguments {
83 signed,
84 number_size,
85 bits,
86 };
87
88 operate(action, args, input, head, engine_state.signals())
89 }
90
91 fn examples(&self) -> Vec<Example<'_>> {
92 vec![
93 Example {
94 description: "rotate right a number with 2 bits",
95 example: "17 | bits ror 2",
96 result: Some(Value::test_int(68)),
97 },
98 Example {
99 description: "rotate right a list of numbers of two bytes",
100 example: "[15 33 92] | bits ror 2 --number-bytes 2",
101 result: Some(Value::list(
102 vec![
103 Value::test_int(49155),
104 Value::test_int(16392),
105 Value::test_int(23),
106 ],
107 Span::test_data(),
108 )),
109 },
110 Example {
111 description: "rotate right binary data",
112 example: "0x[ff bb 03] | bits ror 10",
113 result: Some(Value::binary(vec![0xc0, 0xff, 0xee], Span::test_data())),
114 },
115 ]
116 }
117}
118
119fn action(input: &Value, args: &Arguments, span: Span) -> Value {
120 let Arguments {
121 signed,
122 number_size,
123 bits,
124 } = *args;
125 let bits_span = bits.span;
126 let bits = bits.item;
127
128 match input {
129 Value::Int { val, .. } => {
130 use InputNumType::*;
131 let val = *val;
132 let bits = bits as u32;
133 let input_num_type = get_input_num_type(val, signed, number_size);
134
135 if bits > input_num_type.num_bits() {
136 return Value::error(
137 ShellError::IncorrectValue {
138 msg: format!(
139 "Trying to rotate by more than the available bits ({})",
140 input_num_type.num_bits()
141 ),
142 val_span: bits_span,
143 call_span: span,
144 },
145 span,
146 );
147 }
148 let int = match input_num_type {
149 One => (val as u8).rotate_right(bits) as i64,
150 Two => (val as u16).rotate_right(bits) as i64,
151 Four => (val as u32).rotate_right(bits) as i64,
152 Eight => {
153 let Ok(i) = i64::try_from((val as u64).rotate_right(bits)) else {
154 return Value::error(
155 ShellError::GenericError {
156 error: "result out of range for specified number".into(),
157 msg: format!(
158 "rotating right by {bits} is out of range for the value {val}"
159 ),
160 span: Some(span),
161 help: None,
162 inner: vec![],
163 },
164 span,
165 );
166 };
167 i
168 }
169 SignedOne => (val as i8).rotate_right(bits) as i64,
170 SignedTwo => (val as i16).rotate_right(bits) as i64,
171 SignedFour => (val as i32).rotate_right(bits) as i64,
172 SignedEight => val.rotate_right(bits),
173 };
174
175 Value::int(int, span)
176 }
177 Value::Binary { val, .. } => {
178 let len = val.len();
179 if bits > len * 8 {
180 return Value::error(
181 ShellError::IncorrectValue {
182 msg: format!(
183 "Trying to rotate by more than the available bits ({})",
184 len * 8
185 ),
186 val_span: bits_span,
187 call_span: span,
188 },
189 span,
190 );
191 }
192 let byte_shift = bits / 8;
193 let bit_rotate = bits % 8;
194
195 let bytes = if bit_rotate == 0 {
196 rotate_bytes_right(val, byte_shift)
197 } else {
198 rotate_bytes_and_bits_right(val, byte_shift, bit_rotate)
199 };
200
201 Value::binary(bytes, span)
202 }
203 Value::Error { .. } => input.clone(),
205 other => Value::error(
206 ShellError::OnlySupportsThisInputType {
207 exp_input_type: "int or binary".into(),
208 wrong_type: other.get_type().to_string(),
209 dst_span: span,
210 src_span: other.span(),
211 },
212 span,
213 ),
214 }
215}
216
217fn rotate_bytes_right(data: &[u8], byte_shift: usize) -> Vec<u8> {
218 let len = data.len();
219 let mut output = vec![0; len];
220 output[byte_shift..].copy_from_slice(&data[..len - byte_shift]);
221 output[..byte_shift].copy_from_slice(&data[len - byte_shift..]);
222 output
223}
224
225fn rotate_bytes_and_bits_right(data: &[u8], byte_shift: usize, bit_shift: usize) -> Vec<u8> {
226 debug_assert!(byte_shift < data.len());
227 debug_assert!(
228 (1..8).contains(&bit_shift),
229 "Bit shifts of 0 can't be handled by this impl and everything else should be part of the byteshift"
230 );
231 let mut bytes = Vec::with_capacity(data.len());
232 let mut previous_index = data.len() - byte_shift - 1;
233 for _ in 0..data.len() {
234 let previous_byte = data[previous_index];
235 previous_index += 1;
236 if previous_index == data.len() {
237 previous_index = 0;
238 }
239 let curr_byte = data[previous_index];
240 let rotated_byte = (curr_byte >> bit_shift) | (previous_byte << (8 - bit_shift));
241 bytes.push(rotated_byte);
242 }
243
244 bytes
245}
246#[cfg(test)]
247mod test {
248 use super::*;
249
250 #[test]
251 fn test_examples() {
252 use crate::test_examples;
253
254 test_examples(BitsRor {})
255 }
256}