github.com/cybriq/giocore@v0.0.7-0.20210703034601-cfb9cb5f3900/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/cybriq/giocore/f32" 9 "github.com/cybriq/giocore/internal/opconst" 10 "github.com/cybriq/giocore/op" 11 ) 12 13 // Reader parses an ops list. 14 type Reader struct { 15 pc PC 16 stack []macro 17 ops *op.Ops 18 deferOps op.Ops 19 deferDone bool 20 } 21 22 // EncodedOp represents an encoded op returned by 23 // Reader. 24 type EncodedOp struct { 25 Key Key 26 Data []byte 27 Refs []interface{} 28 } 29 30 // Key is a unique key for a given op. 31 type Key struct { 32 ops *op.Ops 33 pc int 34 version int 35 sx, hx, sy, hy float32 36 } 37 38 // Shadow of op.MacroOp. 39 type macroOp struct { 40 ops *op.Ops 41 pc PC 42 } 43 44 // PC is an instruction counter for an operation list. 45 type PC struct { 46 data int 47 refs int 48 } 49 50 type macro struct { 51 ops *op.Ops 52 retPC PC 53 endPC PC 54 } 55 56 type opMacroDef struct { 57 endpc PC 58 } 59 60 // Reset start reading from the beginning of ops. 61 func (r *Reader) Reset(ops *op.Ops) { 62 r.ResetAt(ops, PC{}) 63 } 64 65 // ResetAt is like Reset, except it starts reading from pc. 66 func (r *Reader) ResetAt(ops *op.Ops, pc PC) { 67 r.stack = r.stack[:0] 68 r.deferOps.Reset() 69 r.deferDone = false 70 r.pc = pc 71 r.ops = ops 72 } 73 74 // NewPC returns a PC representing the current instruction counter of 75 // ops. 76 func NewPC(ops *op.Ops) PC { 77 return PC{ 78 data: len(ops.Data()), 79 refs: len(ops.Refs()), 80 } 81 } 82 83 func (k Key) SetTransform(t f32.Affine2D) Key { 84 sx, hx, _, hy, sy, _ := t.Elems() 85 k.sx = sx 86 k.hx = hx 87 k.hy = hy 88 k.sy = sy 89 return k 90 } 91 92 func (r *Reader) Decode() (EncodedOp, bool) { 93 if r.ops == nil { 94 return EncodedOp{}, false 95 } 96 deferring := false 97 for { 98 if len(r.stack) > 0 { 99 b := r.stack[len(r.stack)-1] 100 if r.pc == b.endPC { 101 r.ops = b.ops 102 r.pc = b.retPC 103 r.stack = r.stack[:len(r.stack)-1] 104 continue 105 } 106 } 107 data := r.ops.Data() 108 data = data[r.pc.data:] 109 refs := r.ops.Refs() 110 if len(data) == 0 { 111 if r.deferDone { 112 return EncodedOp{}, false 113 } 114 r.deferDone = true 115 // Execute deferred macros. 116 r.ops = &r.deferOps 117 r.pc = PC{} 118 continue 119 } 120 key := Key{ops: r.ops, pc: r.pc.data, version: r.ops.Version()} 121 t := opconst.OpType(data[0]) 122 n := t.Size() 123 nrefs := t.NumRefs() 124 data = data[:n] 125 refs = refs[r.pc.refs:] 126 refs = refs[:nrefs] 127 switch t { 128 case opconst.TypeDefer: 129 deferring = true 130 r.pc.data += n 131 r.pc.refs += nrefs 132 continue 133 case opconst.TypeAux: 134 // An Aux operations is always wrapped in a macro, and 135 // its length is the remaining space. 136 block := r.stack[len(r.stack)-1] 137 n += block.endPC.data - r.pc.data - opconst.TypeAuxLen 138 data = data[:n] 139 case opconst.TypeCall: 140 if deferring { 141 deferring = false 142 // Copy macro for deferred execution. 143 if t.NumRefs() != 1 { 144 panic("internal error: unexpected number of macro refs") 145 } 146 deferData := r.deferOps.Write1(t.Size(), refs[0]) 147 copy(deferData, data) 148 continue 149 } 150 var op macroOp 151 op.decode(data, refs) 152 macroData := op.ops.Data()[op.pc.data:] 153 if opconst.OpType(macroData[0]) != opconst.TypeMacro { 154 panic("invalid macro reference") 155 } 156 var opDef opMacroDef 157 opDef.decode(macroData[:opconst.TypeMacro.Size()]) 158 retPC := r.pc 159 retPC.data += n 160 retPC.refs += nrefs 161 r.stack = append(r.stack, macro{ 162 ops: r.ops, 163 retPC: retPC, 164 endPC: opDef.endpc, 165 }) 166 r.ops = op.ops 167 r.pc = op.pc 168 r.pc.data += opconst.TypeMacro.Size() 169 r.pc.refs += opconst.TypeMacro.NumRefs() 170 continue 171 case opconst.TypeMacro: 172 var op opMacroDef 173 op.decode(data) 174 r.pc = op.endpc 175 continue 176 } 177 r.pc.data += n 178 r.pc.refs += nrefs 179 return EncodedOp{Key: key, Data: data, Refs: refs}, true 180 } 181 } 182 183 func (op *opMacroDef) decode(data []byte) { 184 if opconst.OpType(data[0]) != opconst.TypeMacro { 185 panic("invalid op") 186 } 187 bo := binary.LittleEndian 188 data = data[:9] 189 dataIdx := int(int32(bo.Uint32(data[1:]))) 190 refsIdx := int(int32(bo.Uint32(data[5:]))) 191 *op = opMacroDef{ 192 endpc: PC{ 193 data: dataIdx, 194 refs: refsIdx, 195 }, 196 } 197 } 198 199 func (m *macroOp) decode(data []byte, refs []interface{}) { 200 if opconst.OpType(data[0]) != opconst.TypeCall { 201 panic("invalid op") 202 } 203 data = data[:9] 204 bo := binary.LittleEndian 205 dataIdx := int(int32(bo.Uint32(data[1:]))) 206 refsIdx := int(int32(bo.Uint32(data[5:]))) 207 *m = macroOp{ 208 ops: refs[0].(*op.Ops), 209 pc: PC{ 210 data: dataIdx, 211 refs: refsIdx, 212 }, 213 } 214 }