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 }