1use num_bigint::BigUint;
2use sp_ipld::Ipld;
3use sp_std::{
4 borrow::ToOwned,
5 fmt,
6 vec::Vec,
7};
8
9use alloc::string::String;
10
11use crate::{
12 defs,
13 ipld_error::IpldError,
14 literal::Literal,
15 parse,
16 prim::bits,
17 term::Term,
18 yatima,
19};
20
21use core::convert::TryFrom;
22
23#[derive(PartialEq, Eq, Clone, Copy, Debug)]
25pub enum BytesOp {
26 Cons,
27 Len,
28 Head,
29 Tail,
30 Take,
31 Drop,
32 Append,
33 Insert,
34 Remove,
35 Index,
36 ToBits,
37}
38
39impl BytesOp {
40 pub fn symbol(self) -> String {
42 match self {
43 Self::Cons => "cons".to_owned(),
44 Self::Len => "len".to_owned(),
45 Self::Head => "head".to_owned(),
46 Self::Tail => "tail".to_owned(),
47 Self::Take => "take".to_owned(),
48 Self::Drop => "drop".to_owned(),
49 Self::Append => "append".to_owned(),
50 Self::Insert => "insert".to_owned(),
51 Self::Remove => "remove".to_owned(),
52 Self::Index => "index".to_owned(),
53 Self::ToBits => "to_Bits".to_owned(),
54 }
55 }
56
57 pub fn from_symbol(x: &str) -> Option<Self> {
59 match x {
60 "cons" => Some(Self::Cons),
61 "len" => Some(Self::Len),
62 "head" => Some(Self::Head),
63 "tail" => Some(Self::Tail),
64 "take" => Some(Self::Take),
65 "drop" => Some(Self::Drop),
66 "append" => Some(Self::Append),
67 "insert" => Some(Self::Insert),
68 "remove" => Some(Self::Remove),
69 "index" => Some(Self::Index),
70 "to_Bits" => Some(Self::ToBits),
71 _ => None,
72 }
73 }
74
75 pub fn type_of(self) -> Term {
77 match self {
78 Self::Cons => yatima!("∀ #U8 #Bytes -> #Bytes"),
79 Self::Len => yatima!("∀ #Bytes -> #Nat"),
80 Self::Head => yatima!("∀ #Bytes -> #U8"),
81 Self::Tail => yatima!("∀ #Bytes -> #Bytes"),
82 Self::Take => yatima!("∀ #Nat #Bytes -> #Bytes"),
83 Self::Drop => yatima!("∀ #Nat #Bytes -> #Bytes"),
84 Self::Append => yatima!("∀ #Bytes #Bytes -> #Bytes"),
85 Self::Insert => yatima!("∀ #U8 #Nat #Bytes -> #Bytes"),
86 Self::Remove => yatima!("∀ #Nat #Bytes -> #Bytes"),
87 Self::Index => yatima!("∀ #Nat #Bytes -> #U8"),
88 Self::ToBits => yatima!("∀ #Nat #Bytes -> #Bits"),
89 }
90 }
91
92 pub fn to_ipld(self) -> Ipld {
94 match self {
95 Self::Cons => Ipld::Integer(0),
96 Self::Len => Ipld::Integer(1),
97 Self::Head => Ipld::Integer(2),
98 Self::Tail => Ipld::Integer(3),
99 Self::Take => Ipld::Integer(4),
100 Self::Drop => Ipld::Integer(5),
101 Self::Append => Ipld::Integer(6),
102 Self::Insert => Ipld::Integer(7),
103 Self::Remove => Ipld::Integer(8),
104 Self::Index => Ipld::Integer(9),
105 Self::ToBits => Ipld::Integer(10),
106 }
107 }
108
109 pub fn from_ipld(ipld: &Ipld) -> Result<Self, IpldError> {
111 match ipld {
112 Ipld::Integer(0) => Ok(Self::Cons),
113 Ipld::Integer(1) => Ok(Self::Len),
114 Ipld::Integer(2) => Ok(Self::Head),
115 Ipld::Integer(3) => Ok(Self::Tail),
116 Ipld::Integer(4) => Ok(Self::Take),
117 Ipld::Integer(5) => Ok(Self::Drop),
118 Ipld::Integer(6) => Ok(Self::Append),
119 Ipld::Integer(7) => Ok(Self::Insert),
120 Ipld::Integer(8) => Ok(Self::Remove),
121 Ipld::Integer(9) => Ok(Self::Index),
122 Ipld::Integer(10) => Ok(Self::ToBits),
123 xs => Err(IpldError::BytesOp(xs.to_owned())),
124 }
125 }
126
127 pub fn arity(self) -> u64 {
129 match self {
130 Self::Cons => 2,
131 Self::Len => 1,
132 Self::Head => 1,
133 Self::Tail => 1,
134 Self::Take => 2,
135 Self::Drop => 2,
136 Self::Append => 2,
137 Self::Insert => 3,
138 Self::Remove => 2,
139 Self::Index => 2,
140 Self::ToBits => 2,
141 }
142 }
143
144 pub fn apply1(self, x: &Literal) -> Option<Literal> {
146 use Literal::*;
147 match (self, x) {
148 (Self::Len, Bytes(xs)) => Some(Nat(xs.len().into())),
149 (Self::Head, Bytes(xs)) => {
150 let x = xs.last();
151 x.map(|x| U8(*x))
152 }
153 (Self::Tail, Bytes(xs)) => Some(Bytes(if xs.is_empty() {
154 vec![]
155 }
156 else {
157 xs[0..xs.len() - 1].to_vec()
158 })),
159 _ => None,
160 }
161 }
162
163 pub fn apply2(self, x: &Literal, y: &Literal) -> Option<Literal> {
165 use Literal::*;
166 match (self, x, y) {
167 (Self::Cons, U8(x), Bytes(xs)) => {
168 let mut xs = xs.clone();
169 xs.push(*x);
170 Some(Bytes(xs))
171 }
172 (Self::Drop, Nat(x), Bytes(xs)) => {
173 let (_, ys) = safe_split(x, xs);
174 Some(Bytes(ys))
175 }
176 (Self::Take, Nat(x), Bytes(xs)) => {
177 let (xs, _) = safe_split(x, xs);
178 Some(Bytes(xs))
179 }
180 (Self::Append, Bytes(xs), Bytes(ys)) => {
181 let mut xs = xs.clone();
182 xs.extend_from_slice(&ys);
183 Some(Bytes(xs))
184 }
185 (Self::Remove, Nat(idx), Bytes(xs)) => {
186 let idx = usize::try_from(idx);
187 match idx {
188 Ok(idx) if idx < xs.len() => {
189 let mut xs = xs.clone();
190 xs.remove(idx);
191 Some(Bytes(xs))
192 }
193 _ => Some(Bytes(xs.clone())),
194 }
195 }
196 (Self::Index, Nat(idx), Bytes(xs)) => {
197 let idx = usize::try_from(idx);
198 match idx {
199 Ok(idx) if idx < xs.len() => {
200 let x = xs[idx];
201 Some(U8(x))
202 }
203 _ => None,
204 }
205 }
206 (Self::ToBits, Nat(idx), Bytes(xs)) => match usize::try_from(idx) {
207 Ok(x) => Some(Literal::Bits(bits::bytes_to_bits(x, xs))),
208 _ => None,
209 },
210 _ => None,
211 }
212 }
213
214 pub fn apply3(
216 self,
217 x: &Literal,
218 y: &Literal,
219 z: &Literal,
220 ) -> Option<Literal> {
221 use Literal::*;
222 match (self, x, y, z) {
223 (Self::Insert, Nat(idx), U8(y), Bytes(xs)) => {
224 let idx = usize::try_from(idx);
225 match idx {
226 Ok(idx) if idx < xs.len() => {
227 let mut xs = xs.clone();
228 xs.insert(idx, *y);
229 Some(Bytes(xs))
230 }
231 _ => Some(Bytes(xs.clone())),
232 }
233 }
234 _ => None,
235 }
236 }
237}
238
239pub fn safe_split(idx: &BigUint, xs: &Vec<u8>) -> (Vec<u8>, Vec<u8>) {
241 let idx = usize::try_from(idx);
242 match idx {
243 Ok(idx) if idx <= xs.len() => {
244 let ys = xs[0..idx].to_vec();
245 let zs = xs[idx..].to_vec();
246 (ys, zs)
247 }
248 _ => (xs.clone(), vec![]),
249 }
250}
251
252impl fmt::Display for BytesOp {
253 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254 write!(f, "{}", self.symbol())
255 }
256}
257
258#[cfg(test)]
259pub mod tests {
260 use super::*;
261 use crate::prim::{
262 tests::TestArg3,
263 BitsOp,
264 BytesOp,
265 };
266 use quickcheck::{
267 Arbitrary,
268 Gen,
269 TestResult,
270 };
271 use rand::Rng;
272 use sp_std::{
273 convert::TryInto,
274 mem,
275 };
276 use Literal::{
277 Bits,
278 Bytes,
279 Nat,
280 U8,
281 };
282 impl Arbitrary for BytesOp {
283 fn arbitrary(_g: &mut Gen) -> Self {
284 let mut rng = rand::thread_rng();
285 let gen: u32 = rng.gen_range(0..=10);
286 match gen {
287 0 => Self::Cons,
288 1 => Self::Len,
289 2 => Self::Head,
290 3 => Self::Tail,
291 4 => Self::Take,
292 5 => Self::Drop,
293 6 => Self::Append,
294 7 => Self::Insert,
295 8 => Self::Remove,
296 9 => Self::Index,
297 _ => Self::ToBits,
298 }
299 }
300 }
301
302 #[quickcheck]
303 fn bytes_op_ipld(x: BytesOp) -> bool {
304 match BytesOp::from_ipld(&x.to_ipld()) {
305 Ok(y) => x == y,
306 _ => false,
307 }
308 }
309
310 #[quickcheck]
311 fn test_apply(
312 op: BytesOp,
313 a: Vec<u8>,
314 b: u8,
315 c: u64,
316 d: Vec<u8>,
317 ) -> TestResult {
318 let big = BigUint::from;
319 let apply1_bytes = |expected: Option<Literal>| -> TestResult {
320 TestResult::from_bool(BytesOp::apply1(op, &Bytes(a.clone())) == expected)
321 };
322
323 let apply2_u8_bytes = |expected: Option<Literal>| -> TestResult {
324 TestResult::from_bool(
325 BytesOp::apply2(op, &U8(b), &Bytes(a.clone())) == expected,
326 )
327 };
328
329 let apply2_nat_bytes = |expected: Option<Literal>| -> TestResult {
330 TestResult::from_bool(
331 BytesOp::apply2(op, &Nat(big(c)), &Bytes(a.clone())) == expected,
332 )
333 };
334
335 let apply2_bytes_bytes = |expected: Option<Literal>| -> TestResult {
336 TestResult::from_bool(
337 BytesOp::apply2(op, &Bytes(a.clone()), &Bytes(d.clone())) == expected,
338 )
339 };
340
341 let apply3_nat_u8_bytes = |expected: Option<Literal>| -> TestResult {
342 TestResult::from_bool(
343 BytesOp::apply3(op, &Nat(big(c)), &U8(b), &Bytes(a.clone()))
344 == expected,
345 )
346 };
347
348 let from_bool = TestResult::from_bool;
349
350 match op {
351 BytesOp::Cons => {
352 let mut a = a.clone();
353 a.push(b);
354 apply2_u8_bytes(Some(Bytes(a)))
355 }
356 BytesOp::Len => apply1_bytes(Some(Nat(a.len().into()))),
357 BytesOp::Head => apply1_bytes(a.last().map(|z| U8(*z))),
358 BytesOp::Tail => apply1_bytes(Some(Bytes(if a.is_empty() {
359 vec![]
360 }
361 else {
362 a[0..a.len() - 1].to_vec()
363 }))),
364 BytesOp::Take => apply2_nat_bytes(Some(Bytes(safe_split(&big(c), &a).0))),
365 BytesOp::Drop => apply2_nat_bytes(Some(Bytes(safe_split(&big(c), &a).1))),
366 BytesOp::Append => {
367 let mut a = a.clone();
368 a.extend_from_slice(&d);
369 apply2_bytes_bytes(Some(Bytes(a)))
370 }
371 BytesOp::Insert => {
372 let idx = usize::try_from(c);
373 match idx {
374 Ok(idx) if idx < a.len() => {
375 let mut a = a.clone();
376 a.insert(idx, b);
377 apply3_nat_u8_bytes(Some(Bytes(a)))
378 }
379 _ => apply3_nat_u8_bytes(Some(Bytes(a.clone()))),
380 }
381 }
382 BytesOp::Remove => {
383 let idx = usize::try_from(c);
384 match idx {
385 Ok(idx) if idx < a.len() => {
386 let mut a = a.clone();
387 a.remove(idx);
388 apply2_nat_bytes(Some(Bytes(a)))
389 }
390 _ => apply2_nat_bytes(Some(Bytes(a.clone()))),
391 }
392 }
393 BytesOp::Index => {
394 let idx = usize::try_from(c);
395 match idx {
396 Ok(idx) if idx < a.len() => apply2_nat_bytes(Some(U8(a[idx]))),
397 _ => apply2_nat_bytes(None),
398 }
399 }
400 BytesOp::ToBits => {
401 let bits = BytesOp::apply1(op, &Bytes(a.clone()));
402 match bits {
403 None => apply1_bytes(None),
404 Some(bits_) => match bits_.clone() {
405 Bits(_) => {
406 let bytes = BitsOp::apply2(
407 BitsOp::ToBytes,
408 &Nat(big(a.len().try_into().unwrap())),
409 &bits_,
410 );
411 match bytes {
412 None => apply1_bytes(None),
413 Some(bytes_) => from_bool(bytes_ == Bytes(a.clone())),
414 }
415 }
416 _ => apply1_bytes(None),
417 },
418 }
419 }
420 }
421 }
422
423 #[quickcheck]
424 fn test_apply_none_on_invalid(
425 op: BytesOp,
426 a: Literal,
427 b: Vec<u8>,
428 c: u8,
429 d: u64,
430 test_arg_2: bool,
431 test_arg_3: TestArg3,
432 ) -> TestResult {
433 let big = BigUint::from;
434 let test_apply1_none_on_invalid = |valid_arg: Literal| -> TestResult {
435 if mem::discriminant(&valid_arg) == mem::discriminant(&a) {
436 TestResult::discard()
437 }
438 else {
439 TestResult::from_bool(BytesOp::apply1(op, &a) == None)
440 }
441 };
442
443 let test_apply2_none_on_invalid =
444 |valid_arg: Literal, a_: Literal, b_: Literal| -> TestResult {
445 let go =
446 || TestResult::from_bool(BytesOp::apply2(op, &a_, &b_) == None);
447 if test_arg_2 {
448 if mem::discriminant(&valid_arg) == mem::discriminant(&a_) {
449 TestResult::discard()
450 }
451 else {
452 go()
453 }
454 }
455 else {
456 if mem::discriminant(&valid_arg) == mem::discriminant(&b_) {
457 TestResult::discard()
458 }
459 else {
460 go()
461 }
462 }
463 };
464
465 let test_apply3_none_on_invalid = |valid_arg: Literal,
466 a_: Literal,
467 b_: Literal,
468 c_: Literal|
469 -> TestResult {
470 let go =
471 || TestResult::from_bool(BytesOp::apply3(op, &a_, &b_, &c_) == None);
472 match test_arg_3 {
473 TestArg3::A => {
474 if mem::discriminant(&valid_arg) == mem::discriminant(&a_) {
475 TestResult::discard()
476 }
477 else {
478 go()
479 }
480 }
481 TestArg3::B => {
482 if mem::discriminant(&valid_arg) == mem::discriminant(&b_) {
483 TestResult::discard()
484 }
485 else {
486 go()
487 }
488 }
489 TestArg3::C => {
490 if mem::discriminant(&valid_arg) == mem::discriminant(&c_) {
491 TestResult::discard()
492 }
493 else {
494 go()
495 }
496 }
497 }
498 };
499
500 match op {
501 BytesOp::Len | BytesOp::Head | BytesOp::Tail => {
503 test_apply1_none_on_invalid(Bytes(b))
504 }
505 BytesOp::Cons => {
507 if test_arg_2 {
508 test_apply2_none_on_invalid(U8(c), a, Bytes(b))
509 }
510 else {
511 test_apply2_none_on_invalid(Bytes(b), U8(c), a)
512 }
513 }
514 BytesOp::Take
516 | BytesOp::Drop
517 | BytesOp::Remove
518 | BytesOp::Index
519 | BytesOp::ToBits => {
520 if test_arg_2 {
521 test_apply2_none_on_invalid(Nat(big(d)), a, Bytes(b))
522 }
523 else {
524 test_apply2_none_on_invalid(Bytes(b), Nat(big(d)), a)
525 }
526 }
527 BytesOp::Append => {
529 if test_arg_2 {
530 test_apply2_none_on_invalid(Bytes(b.clone()), a, Bytes(b))
531 }
532 else {
533 test_apply2_none_on_invalid(Bytes(b.clone()), Bytes(b), a)
534 }
535 }
536 BytesOp::Insert => match test_arg_3 {
538 TestArg3::A => {
539 test_apply3_none_on_invalid(Nat(big(d)), a, U8(c), Bytes(b))
540 }
541 TestArg3::B => {
542 test_apply3_none_on_invalid(U8(c), Nat(big(d)), a, Bytes(b))
543 }
544 TestArg3::C => {
545 test_apply3_none_on_invalid(Bytes(b), Nat(big(d)), U8(c), a)
546 }
547 },
548 }
549 }
550}