From 06217c532056c0bb3c5a808b403bdca108f84e74 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Wed, 11 Dec 2019 22:53:50 +0100 Subject: [PATCH] op: introduce CallOp We'd like to improve the API of Flex, Stack and similar layouts that use MacroOps internall. Unfortunately, the func (m MacroOp) Add(o *Ops) method causes the MacroOp to be allocated on the heap, ruining the nice garbage-free property of layouts. Fortunately, layouts don't need the feature that caused the heap allocation: invoking operation lists different than the current. CallOp separates the invoke-different-list semantic from MacroOp, in preparation for removing the feature from MacroOp. Signed-off-by: Elias Naur --- internal/opconst/ops.go | 5 ++++- internal/ops/reader.go | 32 ++++++++++++++++++++++++++++++++ op/op.go | 22 ++++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/internal/opconst/ops.go b/internal/opconst/ops.go index 6bca3d05..14844751 100644 --- a/internal/opconst/ops.go +++ b/internal/opconst/ops.go @@ -26,6 +26,7 @@ const ( TypeAux TypeClip TypeProfile + TypeCall ) const ( @@ -47,6 +48,7 @@ const ( TypeAuxLen = 1 TypeClipLen = 1 + 4*4 TypeProfileLen = 1 + TypeCallLen = 1 ) func (t OpType) Size() int { @@ -69,12 +71,13 @@ func (t OpType) Size() int { TypeAuxLen, TypeClipLen, TypeProfileLen, + TypeCallLen, }[t-firstOpIndex] } func (t OpType) NumRefs() int { switch t { - case TypeMacro, TypeKeyInput, TypePointerInput, TypeProfile: + case TypeMacro, TypeKeyInput, TypePointerInput, TypeProfile, TypeCall: return 1 case TypeImage: return 2 diff --git a/internal/ops/reader.go b/internal/ops/reader.go index 15d4ce98..dd2b3406 100644 --- a/internal/ops/reader.go +++ b/internal/ops/reader.go @@ -38,6 +38,11 @@ type macroOp struct { pc pc } +// Shadow of op.CallOp. +type callOp struct { + ops *op.Ops +} + type pc struct { data int refs int @@ -94,6 +99,24 @@ func (r *Reader) Decode() (EncodedOp, bool) { block := r.stack[len(r.stack)-1] n += block.endPC.data - r.pc.data - opconst.TypeAuxLen data = data[:n] + case opconst.TypeCall: + var op callOp + op.decode(data, refs) + endPC := pc{ + data: len(op.ops.Data()), + refs: len(op.ops.Refs()), + } + retPC := r.pc + retPC.data += n + retPC.refs += nrefs + r.stack = append(r.stack, macro{ + ops: r.ops, + retPC: retPC, + endPC: endPC, + }) + r.pc = pc{} + r.ops = op.ops + continue case opconst.TypeMacro: var op macroOp op.decode(data, refs) @@ -148,6 +171,15 @@ func (op *opMacroDef) decode(data []byte) { } } +func (m *callOp) decode(data []byte, refs []interface{}) { + if opconst.OpType(data[0]) != opconst.TypeCall { + panic("invalid op") + } + *m = callOp{ + ops: refs[0].(*op.Ops), + } +} + func (m *macroOp) decode(data []byte, refs []interface{}) { if opconst.OpType(data[0]) != opconst.TypeMacro { panic("invalid op") diff --git a/op/op.go b/op/op.go index 493c29aa..354f9a62 100644 --- a/op/op.go +++ b/op/op.go @@ -45,6 +45,12 @@ The StackOp saves the current state to the state stack and restores it later: // Restore the previous transform. stack.Pop() +The CallOp invokes another operation list: + + ops := new(op.Ops) + ops2 := new(op.Ops) + op.CallOp{Ops: ops2}.Add(ops) + The MacroOp records a list of operations to be executed later: ops := new(op.Ops) @@ -105,6 +111,13 @@ type MacroOp struct { pc pc } +// CallOp invokes all the operations from a separate +// operations list. +type CallOp struct { + // Ops is the list of operations to invoke. + Ops *Ops +} + // InvalidateOp requests a redraw at the given time. Use // the zero value to request an immediate redraw. type InvalidateOp struct { @@ -134,6 +147,15 @@ type pc struct { refs int } +// Add the call to the operation list. +func (c CallOp) Add(o *Ops) { + if c.Ops == nil { + return + } + data := o.Write(opconst.TypeCallLen, c.Ops) + data[0] = byte(opconst.TypeCall) +} + // Push (save) the current operations state. func (s *StackOp) Push(o *Ops) { if s.active {