trn_pact/types/
opcode.rs

1// Copyright 2019 Centrality Investments Limited
2// This file is part of Pact.
3//
4// Licensed under the Apache License v2.0;
5// you may not use this file except in compliance with the License.
6// Unless required by applicable law or agreed to in writing, software
7// distributed under the License is distributed on an "AS IS" BASIS,
8// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9// See the License for the specific language governing permissions and
10// limitations under the License.
11
12// You should have received a copy of the Apache License v2.0
13// along with Pact. If not, see:
14//   <https://futureverse.com/licenses/apachev2.txt>
15
16//!
17//! Pact OpCodes
18//!
19use crate::interpreter::InterpErr;
20use alloc::vec::Vec;
21
22#[cfg(feature = "compiler")]
23pub use compiler::*;
24
25// OpCode masks
26const OP_TYPE_MASK: u8 = 0b0010_0000;
27const OP_INVERT_MASK: u8 = 0b0001_0000;
28const OP_LOAD_MASK: u8 = 0b0000_1000;
29const OP_CONJ_MASK: u8 = 0b0000_1111;
30const OP_COMP_MASK: u8 = 0b0000_0111;
31
32const INDEX_LHS_MASK: u8 = 0b1111_0000;
33const INDEX_RHS_MASK: u8 = 0b0000_1111;
34
35const INDEX_LHS_SHIFT: usize = 4;
36const INDEX_RHS_SHIFT: usize = 0;
37
38/// Indicates whether the source of a load is an `Input`
39/// or stored on the compiled `DataTable`
40#[derive(Clone, Copy, PartialEq)]
41pub enum LoadSource {
42    Input,
43    DataTable,
44}
45
46/// A source for a subject for comparison
47pub struct SubjectSource {
48    pub load_source: LoadSource,
49    pub index: u8,
50}
51
52/// Data structure which breaks down the anatomy of an OpCode
53#[cfg_attr(feature = "std", derive(Debug))]
54#[derive(Clone, Copy, PartialEq)]
55pub enum OpCode {
56    COMP(Comparator),
57    CONJ(Conjunction),
58}
59
60/// Comparator OpCode Structure
61#[cfg_attr(feature = "std", derive(Debug))]
62#[derive(Clone, Copy, PartialEq)]
63pub struct Comparator {
64    pub load: OpLoad,
65    pub op: OpComp,
66    pub indices: OpIndices,
67    pub invert: bool,
68}
69
70/// Conjunction OpCode Structure
71#[cfg_attr(feature = "std", derive(Debug))]
72#[derive(Clone, Copy, PartialEq)]
73pub struct Conjunction {
74    pub op: OpConj,
75    pub invert: bool,
76}
77
78/// Comparator OpCode Structure
79#[cfg_attr(feature = "std", derive(Debug))]
80#[derive(Clone, Copy, PartialEq)]
81pub struct OpIndices {
82    pub lhs: u8,
83    pub rhs: u8,
84}
85
86/// Enum to determine whether a comparator OpCode
87/// is comparing input to datatable or input to input
88#[allow(non_camel_case_types)]
89#[cfg_attr(feature = "std", derive(Debug))]
90#[derive(Clone, Copy, PartialEq)]
91pub enum OpLoad {
92    INPUT_VS_USER,
93    INPUT_VS_INPUT,
94}
95
96impl From<u8> for OpLoad {
97    fn from(value: u8) -> Self {
98        match value {
99            0 => Self::INPUT_VS_USER,
100            1 => Self::INPUT_VS_INPUT,
101            _ => Self::INPUT_VS_USER,
102        }
103    }
104}
105
106/// Enum of avaliable comparator OpCode operations
107#[allow(non_camel_case_types)]
108#[cfg_attr(feature = "std", derive(Debug))]
109#[derive(Clone, Copy, PartialEq)]
110pub enum OpComp {
111    EQ,
112    GT,
113    GTE,
114    IN,
115}
116
117impl From<u8> for OpComp {
118    fn from(value: u8) -> Self {
119        match value {
120            0 => Self::EQ,
121            1 => Self::GT,
122            2 => Self::GTE,
123            3 => Self::IN,
124            _ => Self::EQ,
125        }
126    }
127}
128
129/// Enum of avaliable conjunction OpCode operations
130#[allow(non_camel_case_types)]
131#[cfg_attr(feature = "std", derive(Debug))]
132#[derive(Clone, Copy, PartialEq)]
133pub enum OpConj {
134    AND,
135    OR,
136    XOR,
137}
138
139impl From<u8> for OpConj {
140    fn from(value: u8) -> Self {
141        match value {
142            0 => Self::AND,
143            1 => Self::OR,
144            2 => Self::XOR,
145            _ => Self::AND,
146        }
147    }
148}
149
150impl OpCode {
151    // Compiles the OpCode object into one or more bytes
152    pub fn compile(self, stream: &mut Vec<u8>) {
153        stream.push(self.into());
154        match self {
155            OpCode::COMP(comparator) => stream.push(comparator.indices.into()),
156            _ => {}
157        };
158    }
159
160    /// Return the next OpCode by parsing an input byte stream
161    pub fn parse(stream: &mut dyn Iterator<Item = &u8>) -> Result<Option<Self>, InterpErr> {
162        let op_index = stream.next();
163        if op_index.is_none() {
164            // This is a valid EOI
165            return Ok(None);
166        }
167
168        let index = op_index.unwrap();
169
170        // Check if the invert Bit is Set
171        let invert = (index & OP_INVERT_MASK) == OP_INVERT_MASK;
172
173        // Check the Type of OpCode (0 ? comparator : conjunction)
174        match index & OP_TYPE_MASK {
175            0 => {
176                // Comparator
177                // Determine load type
178                let load = match index & OP_LOAD_MASK {
179                    0 => OpLoad::INPUT_VS_USER,
180                    _ => OpLoad::INPUT_VS_INPUT,
181                };
182                // Determine comparator operation
183                let op = match index & OP_COMP_MASK {
184                    0 => OpComp::EQ,
185                    1 => OpComp::GT,
186                    2 => OpComp::GTE,
187                    3 => OpComp::IN,
188                    _ => return Err(InterpErr::InvalidOpCode(*index)),
189                };
190                // Load indices from the stream
191                let indices = if let Some(i) = stream.next() {
192                    Ok(*i)
193                } else {
194                    Err(InterpErr::UnexpectedEOI("expected index"))
195                }?;
196
197                // form and return the comparator OpCode
198                Ok(Some(OpCode::COMP(Comparator {
199                    load: load,
200                    op: op,
201                    indices: OpIndices {
202                        lhs: (indices & INDEX_LHS_MASK) >> INDEX_LHS_SHIFT,
203                        rhs: (indices & INDEX_RHS_MASK) >> INDEX_RHS_SHIFT,
204                    },
205                    invert: invert,
206                })))
207            }
208            _ => {
209                // Conjunction
210                let op = match index & OP_CONJ_MASK {
211                    0 => OpConj::AND,
212                    1 => OpConj::OR,
213                    2 => OpConj::XOR,
214                    _ => return Err(InterpErr::InvalidOpCode(*index)),
215                };
216                // form and return the conjunction OpCode
217                Ok(Some(OpCode::CONJ(Conjunction {
218                    op: op,
219                    invert: invert,
220                })))
221            }
222        }
223    }
224}
225
226impl Comparator {
227    // Constructor for `Comparator`
228    pub fn new(op: OpComp) -> Self {
229        Comparator {
230            load: OpLoad::INPUT_VS_USER,
231            op: op,
232            indices: OpIndices { lhs: 0, rhs: 0 },
233            invert: false,
234        }
235    }
236
237    // Update the `load` field
238    pub fn load(mut self, load: OpLoad) -> Self {
239        self.load = load;
240        self
241    }
242
243    // Update the `indices` field
244    pub fn indices(mut self, lhs: u8, rhs: u8) -> Self {
245        self.indices.lhs = lhs;
246        self.indices.rhs = rhs;
247        self
248    }
249
250    // Update the `invert` field
251    pub fn invert(mut self) -> Self {
252        self.invert = true;
253        self
254    }
255
256    // Update the `load` field based on a subject set
257    // If lhs = `DataTable` and rhs = `Input`, we need to change sides so that
258    // lhs = `Input` and rhs = `DataTable` as per the `OpCode` encoding spec
259    pub fn loads_from_subjects(mut self, lhs: SubjectSource, rhs: SubjectSource) -> Self {
260        // Determine the Load Order
261        let (load, flip) = match (lhs.load_source, rhs.load_source) {
262            (LoadSource::Input, LoadSource::Input) => (OpLoad::INPUT_VS_INPUT, false),
263            (LoadSource::Input, LoadSource::DataTable) => (OpLoad::INPUT_VS_USER, false),
264            (LoadSource::DataTable, LoadSource::Input) => (OpLoad::INPUT_VS_USER, true),
265            (_, _) => (OpLoad::INPUT_VS_USER, true), // Should not reach here
266        };
267
268        // Apply the load and indices
269        self = self.load(load).indices(lhs.index, rhs.index);
270
271        // Apply a flip if neccessary
272        if flip {
273            self.flip_indices()
274        } else {
275            self
276        }
277    }
278
279    // Flips the lhs and rhs indices and applies any necessary changes to the `op` and
280    // `invert` parameters to keep the expressions consistent
281    pub fn flip_indices(mut self) -> Self {
282        self.indices = OpIndices {
283            lhs: self.indices.rhs,
284            rhs: self.indices.lhs,
285        };
286        let (op, invert) = match self.op {
287            OpComp::EQ => (self.op, self.invert),
288            OpComp::IN => (self.op, self.invert),
289            OpComp::GT => (OpComp::GTE, !self.invert),
290            OpComp::GTE => (OpComp::GT, !self.invert),
291        };
292        self.op = op;
293        self.invert = invert;
294        self
295    }
296}
297
298impl Conjunction {
299    // Constructor for `Conjunction`
300    pub fn new(op: OpConj) -> Self {
301        Conjunction {
302            op: op,
303            invert: false,
304        }
305    }
306
307    // Update the `invert` field
308    pub fn invert(mut self) -> Self {
309        self.invert = true;
310        self
311    }
312}
313
314// For builds which include the compiler
315#[cfg(feature = "compiler")]
316mod compiler {
317    use super::*;
318    use crate::parser::ast;
319    use core::convert::From;
320
321    impl Comparator {
322        // Applies an `ast::Imperative` to the `invert` parameter
323        pub fn apply_imperative(mut self, imperative: &ast::Imperative) -> Self {
324            match imperative {
325                ast::Imperative::MustBe => {}
326                ast::Imperative::MustNotBe => self.invert = !self.invert,
327            };
328            self
329        }
330    }
331
332    impl From<&ast::Comparator> for Comparator {
333        // Creates a `Comparator` from an `ast::Comparator` type
334        fn from(comparator: &ast::Comparator) -> Self {
335            match comparator {
336                ast::Comparator::Equal => Comparator::new(OpComp::EQ),
337                ast::Comparator::GreaterThan => Comparator::new(OpComp::GT),
338                ast::Comparator::GreaterThanOrEqual => Comparator::new(OpComp::GTE),
339                ast::Comparator::LessThan => Comparator::new(OpComp::GTE).invert(),
340                ast::Comparator::LessThanOrEqual => Comparator::new(OpComp::GT).invert(),
341                ast::Comparator::OneOf => Comparator::new(OpComp::IN),
342            }
343        }
344    }
345
346    impl From<&ast::Conjunctive> for Conjunction {
347        // Creates a `Conjunction` from an `ast::Conjunctive` type
348        fn from(conjunctive: &ast::Conjunctive) -> Self {
349            match conjunctive {
350                ast::Conjunctive::And => Conjunction::new(OpConj::AND),
351                ast::Conjunctive::Or => Conjunction::new(OpConj::OR),
352            }
353        }
354    }
355}
356
357impl Into<u8> for OpLoad {
358    fn into(self) -> u8 {
359        match self {
360            OpLoad::INPUT_VS_USER => 0,
361            OpLoad::INPUT_VS_INPUT => OP_LOAD_MASK,
362        }
363    }
364}
365
366impl Into<u8> for OpComp {
367    fn into(self) -> u8 {
368        match self {
369            OpComp::EQ => 0,
370            OpComp::GT => 1,
371            OpComp::GTE => 2,
372            OpComp::IN => 3,
373        }
374    }
375}
376
377impl Into<u8> for OpConj {
378    fn into(self) -> u8 {
379        match self {
380            OpConj::AND => 0,
381            OpConj::OR => 1,
382            OpConj::XOR => 2,
383        }
384    }
385}
386
387impl Into<u8> for OpIndices {
388    fn into(self) -> u8 {
389        (self.lhs << INDEX_LHS_SHIFT) & INDEX_LHS_MASK
390            | (self.rhs << INDEX_RHS_SHIFT) & INDEX_RHS_MASK
391    }
392}
393
394/// Convert an OpCode into its u8 bytecode
395impl Into<u8> for OpCode {
396    fn into(self) -> u8 {
397        match self {
398            OpCode::COMP(comp) => {
399                let invert_u8: u8 = if comp.invert { OP_INVERT_MASK } else { 0 };
400                let load_u8: u8 = comp.load.into();
401                let comp_u8: u8 = comp.op.into();
402                invert_u8 | load_u8 | comp_u8
403            }
404            OpCode::CONJ(conj) => {
405                let invert_u8: u8 = if conj.invert { OP_INVERT_MASK } else { 0 };
406                let conj_u8: u8 = conj.op.into();
407                OP_TYPE_MASK | invert_u8 | conj_u8
408            }
409        }
410    }
411}
412
413#[cfg(test)]
414mod test {
415    use super::*;
416
417    #[test]
418    fn compile_comparators_basic() {
419        let mut bytes = Vec::<u8>::default();
420        OpCode::COMP(Comparator::new(OpComp::EQ)).compile(&mut bytes);
421        OpCode::COMP(Comparator::new(OpComp::GT)).compile(&mut bytes);
422        OpCode::COMP(Comparator::new(OpComp::GTE)).compile(&mut bytes);
423        OpCode::COMP(Comparator::new(OpComp::IN)).compile(&mut bytes);
424        assert_eq!(bytes, vec![0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00,]);
425    }
426
427    #[test]
428    fn compile_conjunctions_basic() {
429        let mut bytes = Vec::<u8>::default();
430        OpCode::CONJ(Conjunction::new(OpConj::AND)).compile(&mut bytes);
431        OpCode::CONJ(Conjunction::new(OpConj::OR)).compile(&mut bytes);
432        OpCode::CONJ(Conjunction::new(OpConj::XOR)).compile(&mut bytes);
433        assert_eq!(bytes, vec![0x20, 0x21, 0x22,]);
434    }
435
436    #[test]
437    fn compile_comparator_advanced() {
438        let mut bytes = Vec::<u8>::default();
439        OpCode::COMP(
440            Comparator::new(OpComp::EQ)
441                .load(OpLoad::INPUT_VS_INPUT)
442                .invert()
443                .indices(11, 3),
444        )
445        .compile(&mut bytes);
446        assert_eq!(bytes, vec![0x18, 0xb3]);
447    }
448
449    #[test]
450    fn compile_conjunction_advanced() {
451        let mut bytes = Vec::<u8>::default();
452        OpCode::CONJ(Conjunction::new(OpConj::OR).invert()).compile(&mut bytes);
453        assert_eq!(bytes, vec![0x31]);
454    }
455
456    #[test]
457    fn parse_comparator_basic() {
458        let mut stream = [0x00_u8, 0x00_u8].iter();
459        let op_code = OpCode::parse(&mut stream).unwrap();
460        assert_eq!(op_code, Some(OpCode::COMP(Comparator::new(OpComp::EQ))));
461    }
462
463    #[test]
464    fn parse_comparator_gt() {
465        let mut stream = [0x01_u8, 0x00_u8].iter();
466        let op_code = OpCode::parse(&mut stream).unwrap();
467        assert_eq!(op_code, Some(OpCode::COMP(Comparator::new(OpComp::GT))));
468    }
469
470    #[test]
471    fn parse_comparator_indicies() {
472        let mut stream = [0x00_u8, 0x5c_u8].iter();
473        let op_code = OpCode::parse(&mut stream).unwrap();
474        assert_eq!(
475            op_code,
476            Some(OpCode::COMP(Comparator::new(OpComp::EQ).indices(5, 12)))
477        );
478    }
479
480    #[test]
481    fn parse_comparator_advanced() {
482        let mut stream = [0x18_u8, 0x27_u8].iter();
483        let op_code = OpCode::parse(&mut stream).unwrap();
484        assert_eq!(
485            op_code,
486            Some(OpCode::COMP(
487                Comparator::new(OpComp::EQ)
488                    .invert()
489                    .load(OpLoad::INPUT_VS_INPUT)
490                    .indices(2, 7)
491            ))
492        );
493    }
494
495    #[test]
496    fn parse_comparator_invalid() {
497        let mut stream = [0x07_u8, 0x00_u8].iter();
498        assert_eq!(
499            OpCode::parse(&mut stream),
500            Err(InterpErr::InvalidOpCode(0x07))
501        );
502    }
503
504    #[test]
505    fn parse_comparator_missing_indices() {
506        let mut stream = [0x00_u8].iter();
507        assert_eq!(
508            OpCode::parse(&mut stream),
509            Err(InterpErr::UnexpectedEOI("expected index"))
510        );
511    }
512
513    #[test]
514    fn parse_conjunction_basic() {
515        let mut stream = [0x20_u8].iter();
516        let op_code = OpCode::parse(&mut stream).unwrap();
517        assert_eq!(op_code, Some(OpCode::CONJ(Conjunction::new(OpConj::AND))));
518    }
519
520    #[test]
521    fn parse_conjunction_xor() {
522        let mut stream = [0x22_u8].iter();
523        let op_code = OpCode::parse(&mut stream).unwrap();
524        assert_eq!(op_code, Some(OpCode::CONJ(Conjunction::new(OpConj::XOR))));
525    }
526
527    #[test]
528    fn parse_conjunction_advanced() {
529        let mut stream = [0x31_u8].iter();
530        assert_eq!(
531            OpCode::parse(&mut stream).unwrap(),
532            Some(OpCode::CONJ(Conjunction::new(OpConj::OR).invert()))
533        );
534    }
535
536    #[test]
537    fn parse_conjunction_invalid() {
538        let mut stream = [0x2f_u8].iter();
539        assert_eq!(
540            OpCode::parse(&mut stream),
541            Err(InterpErr::InvalidOpCode(0x2f))
542        );
543    }
544}