gioui.org/ui@v0.0.0-20190926171558-ce74bc0cbaea/internal/ops/reader.go (about)

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