github.com/gop9/olt@v0.0.0-20200202132135-d956aad50b08/gio/internal/ops/reader.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package ops
     4  
     5  import (
     6  	"encoding/binary"
     7  
     8  	"github.com/gop9/olt/gio/internal/opconst"
     9  	"github.com/gop9/olt/gio/op"
    10  )
    11  
    12  // Reader parses an ops list.
    13  type Reader struct {
    14  	pc    pc
    15  	stack []macro
    16  	ops   *op.Ops
    17  }
    18  
    19  // EncodedOp represents an encoded op returned by
    20  // Reader.
    21  type EncodedOp struct {
    22  	Key  Key
    23  	Data []byte
    24  	Refs []interface{}
    25  }
    26  
    27  // Key is a unique key for a given op.
    28  type Key struct {
    29  	ops     *op.Ops
    30  	pc      int
    31  	version int
    32  }
    33  
    34  // Shadow of op.MacroOp.
    35  type macroOp struct {
    36  	pc pc
    37  }
    38  
    39  // Shadow of op.CallOp.
    40  type callOp struct {
    41  	ops *op.Ops
    42  }
    43  
    44  type pc struct {
    45  	data int
    46  	refs int
    47  }
    48  
    49  type macro struct {
    50  	ops   *op.Ops
    51  	retPC pc
    52  	endPC pc
    53  }
    54  
    55  type opMacroDef struct {
    56  	endpc pc
    57  }
    58  
    59  // Reset start reading from the op list.
    60  func (r *Reader) Reset(ops *op.Ops) {
    61  	r.stack = r.stack[:0]
    62  	r.pc = pc{}
    63  	r.ops = ops
    64  }
    65  
    66  func (r *Reader) Decode() (EncodedOp, bool) {
    67  	if r.ops == nil {
    68  		return EncodedOp{}, false
    69  	}
    70  	for {
    71  		if len(r.stack) > 0 {
    72  			b := r.stack[len(r.stack)-1]
    73  			if r.pc == b.endPC {
    74  				r.ops = b.ops
    75  				r.pc = b.retPC
    76  				r.stack = r.stack[:len(r.stack)-1]
    77  				continue
    78  			}
    79  		}
    80  		data := r.ops.Data()
    81  		data = data[r.pc.data:]
    82  		if len(data) == 0 {
    83  			return EncodedOp{}, false
    84  		}
    85  		key := Key{ops: r.ops, pc: r.pc.data, version: r.ops.Version()}
    86  		t := opconst.OpType(data[0])
    87  		n := t.Size()
    88  		nrefs := t.NumRefs()
    89  		data = data[:n]
    90  		refs := r.ops.Refs()
    91  		refs = refs[r.pc.refs:]
    92  		refs = refs[:nrefs]
    93  		switch t {
    94  		case opconst.TypeAux:
    95  			// An Aux operations is always wrapped in a macro, and
    96  			// its length is the remaining space.
    97  			block := r.stack[len(r.stack)-1]
    98  			n += block.endPC.data - r.pc.data - opconst.TypeAuxLen
    99  			data = data[:n]
   100  		case opconst.TypeCall:
   101  			var op callOp
   102  			op.decode(data, refs)
   103  			endPC := pc{
   104  				data: len(op.ops.Data()),
   105  				refs: len(op.ops.Refs()),
   106  			}
   107  			retPC := r.pc
   108  			retPC.data += n
   109  			retPC.refs += nrefs
   110  			r.stack = append(r.stack, macro{
   111  				ops:   r.ops,
   112  				retPC: retPC,
   113  				endPC: endPC,
   114  			})
   115  			r.pc = pc{}
   116  			r.ops = op.ops
   117  			continue
   118  		case opconst.TypeMacro:
   119  			var op macroOp
   120  			op.decode(data)
   121  			macroData := r.ops.Data()[op.pc.data:]
   122  			if opconst.OpType(macroData[0]) != opconst.TypeMacroDef {
   123  				panic("invalid macro reference")
   124  			}
   125  			var opDef opMacroDef
   126  			opDef.decode(macroData[:opconst.TypeMacroDef.Size()])
   127  			retPC := r.pc
   128  			retPC.data += n
   129  			retPC.refs += nrefs
   130  			r.stack = append(r.stack, macro{
   131  				ops:   r.ops,
   132  				retPC: retPC,
   133  				endPC: opDef.endpc,
   134  			})
   135  			r.pc = op.pc
   136  			r.pc.data += opconst.TypeMacroDef.Size()
   137  			r.pc.refs += opconst.TypeMacroDef.NumRefs()
   138  			continue
   139  		case opconst.TypeMacroDef:
   140  			var op opMacroDef
   141  			op.decode(data)
   142  			r.pc = op.endpc
   143  			continue
   144  		}
   145  		r.pc.data += n
   146  		r.pc.refs += nrefs
   147  		return EncodedOp{Key: key, Data: data, Refs: refs}, true
   148  	}
   149  }
   150  
   151  func (op *opMacroDef) decode(data []byte) {
   152  	if opconst.OpType(data[0]) != opconst.TypeMacroDef {
   153  		panic("invalid op")
   154  	}
   155  	bo := binary.LittleEndian
   156  	dataIdx := int(int32(bo.Uint32(data[1:])))
   157  	refsIdx := int(int32(bo.Uint32(data[5:])))
   158  	*op = opMacroDef{
   159  		endpc: pc{
   160  			data: dataIdx,
   161  			refs: refsIdx,
   162  		},
   163  	}
   164  }
   165  
   166  func (m *callOp) decode(data []byte, refs []interface{}) {
   167  	if opconst.OpType(data[0]) != opconst.TypeCall {
   168  		panic("invalid op")
   169  	}
   170  	*m = callOp{
   171  		ops: refs[0].(*op.Ops),
   172  	}
   173  }
   174  
   175  func (m *macroOp) decode(data []byte) {
   176  	if opconst.OpType(data[0]) != opconst.TypeMacro {
   177  		panic("invalid op")
   178  	}
   179  	bo := binary.LittleEndian
   180  	dataIdx := int(int32(bo.Uint32(data[1:])))
   181  	refsIdx := int(int32(bo.Uint32(data[5:])))
   182  	*m = macroOp{
   183  		pc: pc{
   184  			data: dataIdx,
   185  			refs: refsIdx,
   186  		},
   187  	}
   188  }